package com.jrx.anytxn.investor.service.impl;

import com.jrx.anytxn.common.data.TxnRespCode;
import com.jrx.anytxn.common.exception.TxnBizException;
import com.jrx.anytxn.common.exception.TxnException;
import com.jrx.anytxn.common.function.ToBigDecimalFunction;
import com.jrx.anytxn.common.function.UncheckedConsumer;
import com.jrx.anytxn.common.uitl.*;
import com.jrx.anytxn.feign.param.request.HandleInvestorLimitReq;
import com.jrx.anytxn.feign.param.service.IAnyTxnParamService;
import com.jrx.anytxn.investor.bean.*;
import com.jrx.anytxn.investor.constant.TransBizConstant;
import com.jrx.anytxn.investor.entity.*;
import com.jrx.anytxn.investor.mapper.ext.*;
import com.jrx.anytxn.investor.service.*;
import com.jrx.anytxn.number.exception.SegmentException;
import com.jrx.anytxn.number.service.ISegmentService;
import com.jrx.anytxn.param.constant.Constant;
import com.jrx.anytxn.param.dto.system.SystemParameterRes;
import com.jrx.anytxn.param.entity.PrFundFactor;
import com.jrx.anytxn.param.entity.PrInvestorPenaltyRate;
import com.jrx.anytxn.param.entity.PrInvestorTable;
import com.jrx.anytxn.param.entity.PrProductTable;
import com.jrx.anytxn.param.service.investor.IFundTableService;
import com.jrx.anytxn.param.service.investor.IPrInvestorPenaltyRateService;
import com.jrx.anytxn.param.service.investor.IPrInvestorTableService;
import com.jrx.anytxn.param.service.product.IProductTableService;
import com.jrx.anytxn.param.service.system.ISystemParameterService;
import com.jrx.anytxn.ribbon.config.GrayVersionConfig;
import com.jrx.anytxn.transaction.bean.DelqParamBean;
import com.jrx.anytxn.transaction.constant.TransErrMsgConstant;
import com.jrx.anytxn.transaction.entity.TlPaymentAllocationLog;
import com.jrx.anytxn.transaction.service.IDealDelqService;
import com.jrx.anytxn.param.service.system.ISequenceNumberService;
import com.jrx.anytxn.transaction.service.ITlPaymentAllocationLogService;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;

/**
 * @program: anytxn-cf-transaction
 * @description:
 * @author: xiaoyu.jing
 * @date: 2020-05-22 16:15
 **/
@Service
public class InvestorDealPaymentServiceImpl implements IInvestorDealPaymentService {
    private static final Logger logger = LoggerFactory.getLogger(InvestorDealPaymentServiceImpl.class);

    @Autowired
    private IInvestorTlPaymentAllocationLogService investorTlPaymentAllocationLogService;
    @Autowired
    private ITlPaymentAllocationLogService tlPaymentAllocationLogService;
    @Resource
    private ExtAmAccountMainInvestorInfoMapper extAmAccountMainInvestorInfoMapper;
    @Autowired
    private IInvestorAccountService accountService;
    @Resource
    private ExtAmInterestAccrualInvestorInfoMapper extAmInterestAccrualInvestorInfoMapper;
    @Resource
    private ExtAmLoanAccountInvestorInfoMapper extAmLoanAccountInvestorInfoMapper;
    @Autowired
    private InvestorGlInterfaceServiceImpl glInterfaceService;
    @Resource
    private ExtAmRepaymentPlanInvestorInfoMapper extAmRepaymentPlanInvestorInfoMapper;
    @Autowired
    private IDealDelqService dealDelqService;
    @Resource
    private ExtAmDelinquencyInvestorInfoMapper extAmDelinquencyInvestorInfoMapper;
    @Autowired
    private IPrInvestorPenaltyRateService penaltyRateService;
    @Resource
    private ExtBtInvestorAccountingTransferListMapper extBtInvestorAccountingTransferListMapper;
    @Autowired
    private IInvestorBtAccountingTransferListService btInvestorAccountingTransferListService;
    @Resource
    private ExtTlPaymentAllocationInvestorLogMapper extTlPaymentAllocationInvestorLogMapper;
    @Resource
    private ExtTlGlInterfaceInvestorInfoMapper extTlGlInterfaceInvestorInfoMapper;
    @Autowired
    private IFundTableService fundTableService;
    @Autowired
    private IInvestorFeeService feeService;
    @Autowired
    private IInvestorRepaymentPlanService repaymentPlanService;
    @Autowired
    private IProductTableService productTableService;
    @Autowired
    private ISegmentService segmentService;
    @Autowired
    private IPrInvestorTableService investorTableService;
    @Autowired
    private ExtTlFundTransferLogMapper extTlFundTransferLogMapper;
    @Autowired
    private ExtTlFundTransferDetailMapper extTlFundTransferDetailMapper;

    @Lazy
    @Autowired
    private IAnyTxnParamService anyTxnParamService;

    @Autowired
    private ISystemParameterService systemParameterService;
    @Resource
    private ExtAmLoanMainInvestorInfoMapper extAmLoanMainInvestorInfoMapper;

    @Autowired
    private GrayVersionConfig grayVersionConfig;

    @Autowired
    ISequenceNumberService sequenceNumberService;

    /**
     * 逾期还款
     *
     * @param paymentLog
     * @param bussinessDate 业务日期
     * @throws TxnException
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealOverPay(TlPaymentInvestorLog paymentLog, List<AmLoanMainInvestorInfo> loanMainList, Date bussinessDate) throws Exception {
        String loanId = paymentLog.getLoanId();
        String customerId = paymentLog.getCustomerId();
        String tenantId = paymentLog.getTenantId();
        String custSeqNo = paymentLog.getCustPaymentSeqNo();

        //获取所有资金方贷款订单账户
        List<AmLoanAccountInvestorInfo> loanAcctList = extAmLoanAccountInvestorInfoMapper.findInvestorLoan(loanId, customerId, tenantId);
        //获取所有资金方还款计划
        List<String> planStatusList = Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1,
                TransBizConstant.LOAN_PLAN_STATUS_2);
        List<AmRepaymentPlanInvestorInfo> planList = extAmRepaymentPlanInvestorInfoMapper.selectByLoanIdAndStatus(loanId, planStatusList, customerId, tenantId);
        //获取所有资金方的交易账户
        List<String> statusList = Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_2);
        List<String> typeList = Arrays.asList(TransBizConstant.ACCOUNT_TYPE_03, TransBizConstant.ACCOUNT_TYPE_04,
                TransBizConstant.ACCOUNT_TYPE_06, TransBizConstant.ACCOUNT_TYPE_11, TransBizConstant.ACCOUNT_TYPE_12);
        List<AmAccountMainInvestorInfo> acctList = extAmAccountMainInvestorInfoMapper.selectAccounts(loanId, typeList, customerId, tenantId, statusList);
        //获取所有资金方的累计账户
        List<String> accuAcctTypeList = Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_10, TransBizConstant.INTEREST_ACCRUAL_TYPE_20);
        List<AmInterestAccrualInvestorInfo> accuAcctList = extAmInterestAccrualInvestorInfoMapper.selectAllAccuAcct(
                loanId, customerId, tenantId, accuAcctTypeList, TransBizConstant.ACCOUNT_STATUS_1);
        //获取所有资金方的延滞账户
        List<AmDelinquencyInvestorInfo> delqAcctList = extAmDelinquencyInvestorInfoMapper.selectByLoanIdAndStatus(loanId, customerId, tenantId, TransBizConstant.ACCOUNT_STATUS_1);
        DelqParamBean delqParamBean = dealDelqService.buildDqlqAccountBean(loanMainList.get(0).getTenantId(), loanMainList.get(0).getDelqTableId());


        //生成08交易账户
        TlPaymentAllocationInvestorLog paymentAllocationInvestorLog = investorTlPaymentAllocationLogService.buildTlPaymentAllocationInvestorLog(paymentLog,
                loanMainList.get(0), bussinessDate);

        InvestorPaymentSaveBean saveBean = new InvestorPaymentSaveBean();
        saveBean.setSavaPaymentLog(paymentAllocationInvestorLog);
        //轮询每一个资金源订单获取相应账户
        for (AmLoanMainInvestorInfo loanMain : loanMainList) {
            String investorLoanId = loanMain.getInvestorLoanId();
            AmLoanAccountInvestorInfo loanAcct = loanAcctList.stream()
                    .filter(loan -> loan.getInvestorLoanId().equals(investorLoanId))
                    .findFirst()
                    .orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMain.getInvestorLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });

            //获取交易账户
            this.getAlreadyCastAcct(saveBean, loanMain, acctList);

            //累计账户抛帐
            this.castAccuAcct(saveBean, loanMain, loanAcct, accuAcctList, bussinessDate);
        }

        //获取对客09还款交易账户
        List<TlPaymentAllocationLog> custlog = tlPaymentAllocationLogService.findCustLog(loanId, custSeqNo, customerId, tenantId);
        //获取每期，每个成分的还款
        Map<Integer, Map<String, BigDecimal>> repayMap = custlog.stream()
                .collect(Collectors.groupingBy(TlPaymentAllocationLog::getBillingTenor,
                        Collectors.groupingBy(TlPaymentAllocationLog::getConnectAccountType,
                                CollectorsUtils.summingBigDecimal(TlPaymentAllocationLog::getPostingAmount))));

        //轧差，冲账
        this.repaymentAllocation(saveBean, repayMap, loanMainList, loanAcctList, bussinessDate);

        //生成09对应的gl流水
        List<TlGlInterfaceInvestorInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList());
        saveBean.addSaveGlList(saveGlList);


        InvestorPaymentSaveBean saveBeanCustOrder = new InvestorPaymentSaveBean();
        //循环订单做还款分配后处理
        for (AmLoanMainInvestorInfo loanMain : loanMainList) {
            String investorLoanId = loanMain.getInvestorLoanId();
            AmLoanAccountInvestorInfo loanAcct = loanAcctList.stream().filter(loan -> loan.getInvestorLoanId().equals(investorLoanId)).findFirst().orElseThrow(() -> {
                logger.error("订单交易账户为空，loanId:{}", loanMain.getInvestorLoanId());
                return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
            });
            List<AmRepaymentPlanInvestorInfo> planInfoList = planList.stream()
                    .filter(plan -> investorLoanId.equals(plan.getInvestorLoanId()))
                    .collect(Collectors.toList());

            //生成有后续每个订单处理的saveBean
            InvestorPaymentSaveBean saveBeanPerOrder = this.afterAllocation(saveBean, loanMain);

            // 处理还款计划
            this.dealPlan(saveBeanPerOrder, planInfoList, bussinessDate);

            // 处理订单
            this.dealOrder(saveBeanPerOrder, loanAcct, planInfoList);

            // 处理延滞
            this.dealDelq(saveBeanPerOrder, loanMain, loanAcct, delqAcctList, delqParamBean, bussinessDate);

            PrInvestorTable table = investorTableService.findInvestorByInvestorId(loanMain.getInvestorId(), tenantId);
            // 处理合作行清分
            this.dealTransLog(saveBeanPerOrder, loanMain, table, bussinessDate);

            saveBeanCustOrder.add(saveBeanPerOrder);
        }

        // 入库
        this.saveAllAccts(saveBeanCustOrder);

        // 还款时恢复出资方的日可用额度和总可用额度
        this.recoverInvestorLimit(saveBean.getPaymentLogSaveList(), loanMainList);
    }

    /**
     * 提前结清还款
     *
     * @param paymentLog
     * @param bussinessDate 业务日期
     * @throws TxnException
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealPayOff(TlPaymentInvestorLog paymentLog, List<AmLoanMainInvestorInfo> loanMainList, Date bussinessDate) throws Exception {
        String loanId = paymentLog.getLoanId();
        String customerId = paymentLog.getCustomerId();
        String tenantId = paymentLog.getTenantId();
        String custSeqNo = paymentLog.getCustPaymentSeqNo();


        //获取所有资金方贷款订单账户
        List<AmLoanAccountInvestorInfo> loanAcctList = extAmLoanAccountInvestorInfoMapper.findInvestorLoan(loanId, customerId, tenantId);
        //获取所有资金方还款计划
        List<String> planStatusList = Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1,
                TransBizConstant.LOAN_PLAN_STATUS_2);
        List<AmRepaymentPlanInvestorInfo> planList = extAmRepaymentPlanInvestorInfoMapper.selectByLoanIdAndStatus(loanId, planStatusList, customerId, tenantId);
        //获取所有资金方的交易账户
        List<String> statusList = Arrays.asList(TransBizConstant.ACCOUNT_STATUS_1, TransBizConstant.ACCOUNT_STATUS_2);
        List<String> typeList = Arrays.asList(TransBizConstant.ACCOUNT_TYPE_03, TransBizConstant.ACCOUNT_TYPE_04,
                TransBizConstant.ACCOUNT_TYPE_06, TransBizConstant.ACCOUNT_TYPE_11, TransBizConstant.ACCOUNT_TYPE_12);
        List<AmAccountMainInvestorInfo> acctList = extAmAccountMainInvestorInfoMapper.selectAccounts(loanId, typeList, customerId, tenantId, statusList);
        //获取所有资金方的累计账户
        List<String> accuAcctTypeList = Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_66, TransBizConstant.INTEREST_ACCRUAL_TYPE_10,
                TransBizConstant.INTEREST_ACCRUAL_TYPE_20);
        List<AmInterestAccrualInvestorInfo> accuAcctList = extAmInterestAccrualInvestorInfoMapper.selectAllAccuAcct(
                loanId, customerId, tenantId, accuAcctTypeList, TransBizConstant.ACCOUNT_STATUS_1);
        //获取所有资金方的延滞账户
        List<AmDelinquencyInvestorInfo> delqAcctList = extAmDelinquencyInvestorInfoMapper.selectByLoanIdAndStatus(loanId, customerId, tenantId, TransBizConstant.ACCOUNT_STATUS_1);
        DelqParamBean delqParamBean = dealDelqService.buildDqlqAccountBean(loanMainList.get(0).getTenantId(), loanMainList.get(0).getDelqTableId());

        //生成08交易账户
        TlPaymentAllocationInvestorLog paymentAllocationInvestorLog = investorTlPaymentAllocationLogService.buildTlPaymentAllocationInvestorLog(paymentLog,
                loanMainList.get(0), bussinessDate);

        InvestorPaymentSaveBean saveBean = new InvestorPaymentSaveBean();
        saveBean.setSavaPaymentLog(paymentAllocationInvestorLog);
        //轮询每一个资金源订单获取相应账户
        for (AmLoanMainInvestorInfo loanMain : loanMainList) {
            String investorLoanId = loanMain.getInvestorLoanId();
            AmLoanAccountInvestorInfo loanAcct = loanAcctList.stream()
                    .filter(loan -> loan.getInvestorLoanId().equals(investorLoanId))
                    .findFirst().orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMain.getInvestorLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });

            //获取交易账户
            this.getAlreadyCastAcct(saveBean, loanMain, acctList);

            //累计账户抛帐
            this.castAccuAcct(saveBean, loanMain, loanAcct, accuAcctList, bussinessDate);

            List<AmRepaymentPlanInvestorInfo> planInfoList = planList.stream()
                    .filter(plan -> investorLoanId.equals(plan.getInvestorLoanId()))
                    .collect(Collectors.toList());
            feeService.castPayOffFeeAndIntrAcct(saveBean, loanMain, loanAcct, planInfoList, delqParamBean, bussinessDate);
            this.castNonCastPrinAcct(saveBean, loanMain, loanAcct, planInfoList, delqParamBean, bussinessDate);
        }

        //获取对客09还款交易账户
        List<TlPaymentAllocationLog> custlog = tlPaymentAllocationLogService.findCustLog(loanId, custSeqNo, customerId, tenantId);
        //获取每期，每个成分的还款
        Map<Integer, Map<String, BigDecimal>> repayMap = custlog.stream()
                .collect(Collectors.groupingBy(TlPaymentAllocationLog::getBillingTenor,
                        Collectors.groupingBy(TlPaymentAllocationLog::getConnectAccountType,
                                CollectorsUtils.summingBigDecimal(TlPaymentAllocationLog::getPostingAmount))));

        //轧差，冲账
        this.repaymentAllocation(saveBean, repayMap, loanMainList, loanAcctList, bussinessDate);

        //生成09对应的gl流水
        List<TlGlInterfaceInvestorInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList());
        saveBean.addSaveGlList(saveGlList);


        InvestorPaymentSaveBean saveBeanCustOrder = new InvestorPaymentSaveBean();
        //循环订单做还款分配后处理
        for (AmLoanMainInvestorInfo loanMain : loanMainList) {
            String investorLoanId = loanMain.getInvestorLoanId();
            AmLoanAccountInvestorInfo loanAcct = loanAcctList.stream().filter(loan -> loan.getInvestorLoanId().equals(investorLoanId)).findFirst().orElseThrow(() -> {
                logger.error("订单交易账户为空，loanId:{}", loanMain.getInvestorLoanId());
                return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
            });
            List<AmRepaymentPlanInvestorInfo> planInfoList = planList.stream()
                    .filter(plan -> investorLoanId.equals(plan.getInvestorLoanId()))
                    .collect(Collectors.toList());

            //生成有后续每个订单处理的saveBean
            InvestorPaymentSaveBean saveBeanPerOrder = this.afterAllocation(saveBean, loanMain);

            // 处理还款计划
            this.dealPlan(saveBeanPerOrder, planInfoList, bussinessDate);

            // 处理订单
            this.dealOrder(saveBeanPerOrder, loanAcct, planInfoList);

            // 处理延滞
            this.dealDelq(saveBeanPerOrder, loanMain, loanAcct, delqAcctList, delqParamBean, bussinessDate);

            PrInvestorTable table = investorTableService.findInvestorByInvestorId(loanMain.getInvestorId(), tenantId);
            // 处理合作行清分
            this.dealTransLog(saveBeanPerOrder, loanMain, table, bussinessDate);

            saveBeanCustOrder.add(saveBeanPerOrder);
        }

        // 入库
        this.saveAllAccts(saveBeanCustOrder);

        // 还款时恢复出资方的日可用额度和总可用额度
        this.recoverInvestorLimit(saveBean.getPaymentLogSaveList(), loanMainList);
    }

    /**
     * 正常还款
     *
     * @param paymentLog
     * @param bussinessDate 业务日期
     * @throws TxnException
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealPay(TlPaymentInvestorLog paymentLog, List<AmLoanMainInvestorInfo> loanMainList, Date bussinessDate) throws Exception {
        String loanId = paymentLog.getLoanId();
        String customerId = paymentLog.getCustomerId();
        String tenantId = paymentLog.getTenantId();
        String custSeqNo = paymentLog.getCustPaymentSeqNo();


        //获取所有资金方贷款订单账户
        List<AmLoanAccountInvestorInfo> loanAcctList = extAmLoanAccountInvestorInfoMapper.findInvestorLoan(loanId, customerId, tenantId);
        //获取所有资金方还款计划
        List<String> planStatusList = Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1,
                TransBizConstant.LOAN_PLAN_STATUS_2);
        List<AmRepaymentPlanInvestorInfo> planList = extAmRepaymentPlanInvestorInfoMapper.selectByLoanIdAndStatus(loanId, planStatusList, customerId, tenantId);
        //获取所有资金方的交易账户
        List<String> statusList = Arrays.asList(TransBizConstant.ACCOUNT_STATUS_1, TransBizConstant.ACCOUNT_STATUS_2);
        List<String> typeList = Arrays.asList(TransBizConstant.ACCOUNT_TYPE_03, TransBizConstant.ACCOUNT_TYPE_04,
                TransBizConstant.ACCOUNT_TYPE_06, TransBizConstant.ACCOUNT_TYPE_11, TransBizConstant.ACCOUNT_TYPE_12);
        List<AmAccountMainInvestorInfo> acctList = extAmAccountMainInvestorInfoMapper.selectAccounts(loanId, typeList, customerId, tenantId, statusList);
        //获取所有资金方的累计账户
        List<String> accuAcctTypeList = Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_10, TransBizConstant.INTEREST_ACCRUAL_TYPE_20);
        List<AmInterestAccrualInvestorInfo> accuAcctList = extAmInterestAccrualInvestorInfoMapper.selectAllAccuAcct(
                loanId, customerId, tenantId, accuAcctTypeList, TransBizConstant.ACCOUNT_STATUS_1);
        //获取所有资金方的延滞账户
        List<AmDelinquencyInvestorInfo> delqAcctList = extAmDelinquencyInvestorInfoMapper.selectByLoanIdAndStatus(loanId, customerId, tenantId, TransBizConstant.ACCOUNT_STATUS_1);
        DelqParamBean delqParamBean = dealDelqService.buildDqlqAccountBean(loanMainList.get(0).getTenantId(), loanMainList.get(0).getDelqTableId());


        //生成08交易账户
        TlPaymentAllocationInvestorLog paymentAllocationInvestorLog = investorTlPaymentAllocationLogService.buildTlPaymentAllocationInvestorLog(paymentLog,
                loanMainList.get(0), bussinessDate);

        InvestorPaymentSaveBean saveBean = new InvestorPaymentSaveBean();
        saveBean.setSavaPaymentLog(paymentAllocationInvestorLog);
        //轮询每一个资金源订单获取相应账户
        for (AmLoanMainInvestorInfo loanMain : loanMainList) {
            String investorLoanId = loanMain.getInvestorLoanId();
            AmLoanAccountInvestorInfo loanAcct = loanAcctList.stream()
                    .filter(loan -> investorLoanId.equals(loan.getInvestorLoanId()))
                    .findFirst().orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMain.getInvestorLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });

            //获取交易账户
            this.getAlreadyCastAcct(saveBean, loanMain, acctList);

            //累计账户抛帐
            this.castAccuAcct(saveBean, loanMain, loanAcct, accuAcctList, bussinessDate);
        }

        //获取对客09还款交易账户
        List<TlPaymentAllocationLog> custlog = tlPaymentAllocationLogService.findCustLog(loanId, custSeqNo, customerId, tenantId);
        //获取每期，每个成分的还款
        Map<Integer, Map<String, BigDecimal>> repayMap = custlog.stream()
                .collect(Collectors.groupingBy(TlPaymentAllocationLog::getBillingTenor,
                        Collectors.groupingBy(TlPaymentAllocationLog::getConnectAccountType,
                                CollectorsUtils.summingBigDecimal(TlPaymentAllocationLog::getPostingAmount))));

        //轧差，冲账
        this.repaymentAllocation(saveBean, repayMap, loanMainList, loanAcctList, bussinessDate);

        //生成09对应的gl流水
        List<TlGlInterfaceInvestorInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList());
        saveBean.addSaveGlList(saveGlList);


        InvestorPaymentSaveBean saveBeanCustOrder = new InvestorPaymentSaveBean();
        //循环订单做还款分配后处理
        for (AmLoanMainInvestorInfo loanMain : loanMainList) {
            String investorLoanId = loanMain.getInvestorLoanId();
            AmLoanAccountInvestorInfo loanAcct = loanAcctList.stream().filter(loan -> loan.getInvestorLoanId().equals(investorLoanId)).findFirst().orElseThrow(() -> {
                logger.error("订单交易账户为空，loanId:{}", loanMain.getInvestorLoanId());
                return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
            });
            List<AmRepaymentPlanInvestorInfo> planInfoList = planList.stream()
                    .filter(plan -> investorLoanId.equals(plan.getInvestorLoanId()))
                    .collect(Collectors.toList());

            //生成有后续每个订单处理的saveBean
            InvestorPaymentSaveBean saveBeanPerOrder = this.afterAllocation(saveBean, loanMain);

            // 处理还款计划
            this.dealPlan(saveBeanPerOrder, planInfoList, bussinessDate);

            // 处理订单
            this.dealOrder(saveBeanPerOrder, loanAcct, planInfoList);

            // 处理延滞
            this.dealDelq(saveBeanPerOrder, loanMain, loanAcct, delqAcctList, delqParamBean, bussinessDate);

            PrInvestorTable table = investorTableService.findInvestorByInvestorId(loanMain.getInvestorId(), tenantId);
            // 处理合作行清分
            this.dealTransLog(saveBeanPerOrder, loanMain, table, bussinessDate);

            saveBeanCustOrder.add(saveBeanPerOrder);
        }

        // 入库
        this.saveAllAccts(saveBeanCustOrder);

        // 还款时恢复出资方的日可用额度和总可用额度
        this.recoverInvestorLimit(saveBean.getPaymentLogSaveList(), loanMainList);
    }

    /**
     * 提前还款
     *
     * @param paymentLog
     * @param bussinessDate 业务日期
     * @throws TxnException
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealPrePay(TlPaymentInvestorLog paymentLog, List<AmLoanMainInvestorInfo> loanMainList, Date bussinessDate) throws Exception {
        String loanId = paymentLog.getLoanId();
        String customerId = paymentLog.getCustomerId();
        String tenantId = paymentLog.getTenantId();
        String custSeqNo = paymentLog.getCustPaymentSeqNo();

        //获取所有资金方贷款订单账户
        List<AmLoanAccountInvestorInfo> loanAcctList = extAmLoanAccountInvestorInfoMapper.findInvestorLoan(loanId, customerId, tenantId);
        //获取所有资金方还款计划
        List<String> planStatusList = Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1,
                TransBizConstant.LOAN_PLAN_STATUS_2);
        List<AmRepaymentPlanInvestorInfo> planList = extAmRepaymentPlanInvestorInfoMapper.selectByLoanIdAndStatus(loanId, planStatusList, customerId, tenantId);
        //获取所有资金方的交易账户
        List<String> statusList = Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_1);
        List<String> typeList = Arrays.asList(TransBizConstant.ACCOUNT_TYPE_03, TransBizConstant.ACCOUNT_TYPE_04,
                TransBizConstant.ACCOUNT_TYPE_06, TransBizConstant.ACCOUNT_TYPE_11, TransBizConstant.ACCOUNT_TYPE_12);
        List<AmAccountMainInvestorInfo> acctList = extAmAccountMainInvestorInfoMapper.selectAccounts(loanId, typeList, customerId, tenantId, statusList);
        //获取所有资金方的累计账户
        List<String> accuAcctTypeList = Collections.singletonList(TransBizConstant.INTEREST_ACCRUAL_TYPE_66);
        List<AmInterestAccrualInvestorInfo> accuAcctList = extAmInterestAccrualInvestorInfoMapper.selectAllAccuAcct(
                loanId, customerId, tenantId, accuAcctTypeList, TransBizConstant.ACCOUNT_STATUS_1);
        DelqParamBean delqParamBean = dealDelqService.buildDqlqAccountBean(loanMainList.get(0).getTenantId(), loanMainList.get(0).getDelqTableId());
        //获取资金方信息
        List<PrFundFactor> fundFactorList = fundTableService.findByFundIdAndTenId(loanMainList.get(0).getTenantId(),
                loanMainList.get(0).getFundId()).getControllerReqList();
        PrProductTable prProductTable = productTableService.findProByProIdAndOrgId(loanMainList.get(0).getProductId(),
                loanMainList.get(0).getOrganizationId(), loanMainList.get(0).getTenantId());


        //生成08交易账户
        TlPaymentAllocationInvestorLog paymentAllocationInvestorLog = investorTlPaymentAllocationLogService.buildTlPaymentAllocationInvestorLog(paymentLog,
                loanMainList.get(0), bussinessDate);

        InvestorPaymentSaveBean saveBean = new InvestorPaymentSaveBean();
        saveBean.setSavaPaymentLog(paymentAllocationInvestorLog);

        //获取对客09还款交易账户
        List<TlPaymentAllocationLog> custlog = tlPaymentAllocationLogService.findCustLog(loanId, custSeqNo, customerId, tenantId);
        //获取每期，每个成分的还款
        Map<Integer, Map<String, BigDecimal>> repayMap = custlog.stream()
                .collect(Collectors.groupingBy(TlPaymentAllocationLog::getBillingTenor,
                        Collectors.groupingBy(TlPaymentAllocationLog::getConnectAccountType,
                                CollectorsUtils.summingBigDecimal(TlPaymentAllocationLog::getPostingAmount))));

        BigDecimal totalPrinAmt = BigDecimal.ZERO;
        BigDecimal totalCustPrinAmt = repayMap.values().stream().map(m -> m.get(TransBizConstant.ACCOUNT_TYPE_06))
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        Map<String, BigDecimal> prinBalMap = new HashMap<>();
        //轮询每一个资金源订单获取相应账户
        loanMainList.sort(Comparator.comparing(AmLoanMainInvestorInfo::getOffsetFlag));
        for (AmLoanMainInvestorInfo loanMain : loanMainList) {
            String investorLoanId = loanMain.getInvestorLoanId();
            AmLoanAccountInvestorInfo loanAcct = loanAcctList.stream()
                    .filter(loan -> investorLoanId.equals(loan.getInvestorLoanId()))
                    .findFirst().orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMain.getInvestorLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });

            //获取交易账户
            this.getAlreadyCastAcct(saveBean, loanMain, acctList);

            //累计账户抛帐
            this.castAccuAcct(saveBean, loanMain, loanAcct, accuAcctList, bussinessDate);


            //提前还款费用抛帐
            List<AmRepaymentPlanInvestorInfo> planInfoList = planList.stream()
                    .filter(plan -> investorLoanId.equals(plan.getInvestorLoanId()))
                    .collect(Collectors.toList());
            String liabilityFlag = planInfoList.get(0).getLiabilityFlag();
            feeService.castPrePayFeeAcct(saveBean, loanMain, loanAcct, bussinessDate, liabilityFlag);


            String investorId = loanMain.getInvestorId();
            PrFundFactor fundFactor = fundFactorList.stream().filter(factor -> investorId.equals(factor.getInvestorId())).findFirst().orElseThrow(() -> {
                logger.error("订单交易账户为空，loanId:{}", loanMain.getInvestorLoanId());
                return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
            });
            BigDecimal percentage = fundFactor.getInvertorPercentage();

            //非轧差方还款本金
            BigDecimal repayPrinAmt = totalCustPrinAmt.multiply(percentage);
            if (TransBizConstant.OFFSET_1.equals(loanMain.getOffsetFlag())) {
                repayPrinAmt = totalCustPrinAmt.subtract(totalPrinAmt);
            }
            totalPrinAmt = totalPrinAmt.add(repayPrinAmt);

            BigDecimal afterPayPrinBalAmt = loanAcct.getRemainPrincipal().subtract(repayPrinAmt);
            prinBalMap.put(investorLoanId, afterPayPrinBalAmt);

            //调整当期本金
            adjustPrinAcct(saveBean, loanMain, loanAcct, planInfoList, repayPrinAmt, delqParamBean, bussinessDate);
        }
        logger.info("还款后剩余本金prinBalMap:{}", prinBalMap);

        //轧差，冲账
        this.repaymentAllocation(saveBean, repayMap, loanMainList, loanAcctList, bussinessDate);

        //生成09对应的gl流水
        List<TlGlInterfaceInvestorInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList());
        saveBean.addSaveGlList(saveGlList);


        InvestorPaymentSaveBean saveBeanCustOrder = new InvestorPaymentSaveBean();
        List<AmRepaymentPlanInvestorInfo> unOffSetReplanList = new ArrayList<>();
        //循环订单做还款分配后处理
        for (AmLoanMainInvestorInfo loanMain : loanMainList) {
            String investorLoanId = loanMain.getInvestorLoanId();
            AmLoanAccountInvestorInfo loanAcct = loanAcctList.stream().filter(loan -> loan.getInvestorLoanId().equals(investorLoanId)).findFirst().get();
            List<AmRepaymentPlanInvestorInfo> planInfoList = planList.stream()
                    .filter(plan -> investorLoanId.equals(plan.getInvestorLoanId()))
                    .collect(Collectors.toList());

            //生成有后续每个订单处理的saveBean
            InvestorPaymentSaveBean saveBeanPerOrder = this.afterAllocation(saveBean, loanMain);

            // 处理还款计划
            this.dealPrePayPlan(saveBeanPerOrder, loanMain, loanAcct, planInfoList, unOffSetReplanList, prinBalMap, prProductTable, bussinessDate);

            // 根据重新生成的还款计划，对当期本金，利息累计,分期费用重新抛帐
            this.reCastAcct(saveBeanPerOrder, loanMain, loanAcct, bussinessDate);

            // 处理订单
            this.dealOrder(saveBeanPerOrder, loanAcct, planInfoList);

            PrInvestorTable table = investorTableService.findInvestorByInvestorId(loanMain.getInvestorId(), tenantId);
            // 处理合作行清分
            this.dealTransLog(saveBeanPerOrder, loanMain, table, bussinessDate);


            saveBeanCustOrder.add(saveBeanPerOrder);
        }

        // 入库
        this.saveAllAccts(saveBeanCustOrder);

        // 还款时恢复出资方的日可用额度和总可用额度
        this.recoverInvestorLimit(saveBean.getPaymentLogSaveList(), loanMainList);
    }

    /**
     * 获取已抛帐本金交易，利息交易，罚息交易，罚息复利交易，分期费用交易账户
     *
     * @param saveBean
     * @param loanMain
     * @param acctList
     * @throws TxnBizException
     */
    private void getAlreadyCastAcct(InvestorPaymentSaveBean saveBean, AmLoanMainInvestorInfo loanMain,
                                    List<AmAccountMainInvestorInfo> acctList) throws TxnBizException {
        logger.info("获取本金交易，利息交易，分期费用账户开始");
        String investorLoanId = loanMain.getInvestorLoanId();
        List<AmAccountMainInvestorInfo> accountList = acctList.stream()
                .filter(acct -> investorLoanId.equals(acct.getInvestorLoanId()))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(accountList)) {
            logger.info(TransErrMsgConstant.NO_PAYMENT_ACCT);
            return;
        }

        saveBean.addAcctGroupByTypeMapList(accountList);
        accountList.forEach(saveBean::addUpdateAcct);
        logger.info("获取本金交易，利息交易，分期费用账户结束,updateAcctList.size:{}", saveBean.getUpdateAcctList().size());
    }

    /**
     * 利息/罚息/罚息复利累计抛帐，获取利息/罚息/罚息复利交易，并关闭利息/罚息/罚息复利累计
     *
     * @param saveBean
     * @param loanMain
     * @param loanAcct
     * @param accrualAcctList
     * @param businessDate
     */
    private void castAccuAcct(InvestorPaymentSaveBean saveBean, AmLoanMainInvestorInfo loanMain, AmLoanAccountInvestorInfo loanAcct,
                              List<AmInterestAccrualInvestorInfo> accrualAcctList, Date businessDate) {
        String investorLoanId = loanMain.getInvestorLoanId();
        //获取利息/罚息/罚息复利累计
        List<AmInterestAccrualInvestorInfo> accuAcctList = accrualAcctList.stream()
                .filter(acct -> investorLoanId.equals(acct.getInvestorLoanId()))
                .collect(Collectors.toList());

        if (CollectionUtils.isEmpty(accuAcctList)) {
            return;
        }

        UncheckedConsumer<AmInterestAccrualInvestorInfo> castAccu = accuAcct -> {
            //通过订单，累计生成利息/罚息/罚息复利交易
            InvestorCastAccountTransactionBean intrCastAccrt = accountService.castInterestAccount(loanMain, loanAcct,
                    accuAcct, businessDate);
            logger.info("轮询当期利息／罚息累计，生成的交易账户intrAcct:{}", BeanUtils.toMap(intrCastAccrt.getAmAccountMainInfo()));
            //关闭原利息/罚息累计
            AmInterestAccrualInvestorInfo updateAccuAcct = new AmInterestAccrualInvestorInfo();
            updateAccuAcct.setId(accuAcct.getId());
            updateAccuAcct.setInvestorLoanId(accuAcct.getInvestorLoanId());
            updateAccuAcct.setTenantId(accuAcct.getTenantId());
            updateAccuAcct.setCustomerId(accuAcct.getCustomerId());
            updateAccuAcct.setAccountStatus(TransBizConstant.ACCOUNT_STATUS_8);
            //添加代偿标识
            if (TransBizConstant.COMPENSATORY_FLAG_1.equals(loanAcct.getCompensatoryFlag())) {
                updateAccuAcct.setCompensatoryFlag(TransBizConstant.COMPENSATORY_FLAG_1);
            }
            //保存生成的交易和需要更新的累计
            saveBean.addAcctGroupByTypeMap(intrCastAccrt.getAmAccountMainInfo());
            saveBean.addSaveAcct(intrCastAccrt.getAmAccountMainInfo());
            saveBean.addUpdateAccuAcct(updateAccuAcct);
            saveBean.addSaveGlList(intrCastAccrt.getTlGlInterfaceInfoList());
        };
        //轮询累计账户生成对应的交易账户
        accuAcctList.forEach(CollectorsUtils.tranToUncheck(castAccu));
    }

    /**
     * 未抛帐的本金抛帐，获取本金交易账户
     *
     * @param saveBean
     * @param loanMain
     * @param loanAcct
     * @param planList
     * @param delqParamBean
     * @param bussinessDate
     */
    private void castNonCastPrinAcct(InvestorPaymentSaveBean saveBean, AmLoanMainInvestorInfo loanMain, AmLoanAccountInvestorInfo loanAcct,
                                     List<AmRepaymentPlanInvestorInfo> planList, DelqParamBean delqParamBean, Date bussinessDate) {

        //循环未抛帐的还款计划，生成对应的本金交易账户
        UncheckedConsumer<AmRepaymentPlanInvestorInfo> castPrinAcct = plan -> {
            if (TransBizConstant.LOAN_PLAN_STATUS_0.equals(plan.getPlanStatus())) {
                InvestorCastAccountTransactionBean intrCastAccrt = accountService.castPrincipalAccount(loanMain, loanAcct, plan,
                        delqParamBean, bussinessDate);
                loanAcct.setCastTenor(plan.getBillingTenor());
                saveBean.addSaveAcct(intrCastAccrt.getAmAccountMainInfo());
                saveBean.addAcctGroupByTypeMap(intrCastAccrt.getAmAccountMainInfo());
                saveBean.addSaveGlList(intrCastAccrt.getTlGlInterfaceInfoList());
            }
        };

        planList.forEach(CollectorsUtils.tranToUncheck(castPrinAcct));

    }

    /**
     * 调整当期本金交易账户
     *
     * @param saveBean
     * @param loanMain
     * @param loanAcct
     * @param planInfoList
     * @param repayPrinAmt
     * @param delqParamBean
     * @param bussinessDate
     * @throws TxnException
     */
    private void adjustPrinAcct(InvestorPaymentSaveBean saveBean, AmLoanMainInvestorInfo loanMain, AmLoanAccountInvestorInfo loanAcct,
                                List<AmRepaymentPlanInvestorInfo> planInfoList, BigDecimal repayPrinAmt, DelqParamBean delqParamBean, Date bussinessDate) throws TxnException {
        /*
         *     应还金额与还款金额进行比较。如果还款金额更大，则再生成一个当期的本金交易账户，入账金额为其差值。
         *     如果还款金额更小，则更新当期的本金交易账户的待调金额为其差值。通过实际还款本金得到还款后的剩余本金
         */

        List<AmAccountMainInvestorInfo> upDateAcctList = saveBean.getUpdateAcctList();

        String investorLoanId = loanMain.getInvestorLoanId();
        //当期本金欠款
        AmAccountMainInvestorInfo prinAcct = upDateAcctList.stream()
                .filter(acct -> investorLoanId.equals(acct.getInvestorLoanId()))
                .filter(acct -> loanAcct.getCastTenor().equals(acct.getBillingTenor()))
                .filter(acct -> TransBizConstant.ACCOUNT_TYPE_06.equals(acct.getAccountType()))
                .findFirst().orElse(null);

        //没有本金欠款
        if (null == prinAcct) {
            return;
        }

        BigDecimal remainPrinAmt = prinAcct.getPostingAmount().subtract(prinAcct.getRepayAmount()).subtract(prinAcct.getCreditAdjustAmount());
        //剩余未抛本金
        BigDecimal unCastPrin = loanAcct.getRemainPrincipal().subtract(loanAcct.getPrincipalBalance());
        //还款后剩余金额
        BigDecimal afterRepayBal = repayPrinAmt.subtract(remainPrinAmt).subtract(unCastPrin);
        logger.info("提前还款处理本金交易账户，还款本金:{}，当期本金欠款:{}，剩余未抛本金:{},本次还款后的剩余本金:{}", repayPrinAmt, remainPrinAmt, unCastPrin, afterRepayBal);
        if (afterRepayBal.compareTo(BigDecimal.ZERO) >= 0) {
            // 按提前结清处理，抛出剩余未抛本金
            this.castNonCastPrinAcct(saveBean, loanMain, loanAcct, planInfoList, delqParamBean, bussinessDate);
        } else if (repayPrinAmt.compareTo(remainPrinAmt) > 0) {
            //还款金额 > 已抛账户总欠款 && 还款金额 < 已抛账户总欠款+未抛本金  多出的金额则再生成一个当期的本金交易账户 即溢还款本金交易账户
            //查询当期还款计划
            AmRepaymentPlanInvestorInfo castPlan = planInfoList.stream()
                    .filter(plan -> loanAcct.getCastTenor().equals(plan.getBillingTenor()))
                    .findFirst().get();
            AmRepaymentPlanInvestorInfo newCastPlan = new AmRepaymentPlanInvestorInfo();
            BeanMapping.copy(castPlan, newCastPlan);
            newCastPlan.setPrincipalAmount(repayPrinAmt.subtract(remainPrinAmt));
            InvestorCastAccountTransactionBean prinCastAcct = accountService.castPrincipalAccount(loanMain, loanAcct,
                    newCastPlan, delqParamBean, bussinessDate);
            logger.info("溢出本金交易账户prinAcct:{}", BeanUtils.toMap(prinCastAcct.getAmAccountMainInfo()));
            saveBean.addSaveAcct(prinCastAcct.getAmAccountMainInfo());
            saveBean.addSaveGlList(prinCastAcct.getTlGlInterfaceInfoList());
            saveBean.addAcctGroupByTypeMap(prinCastAcct.getAmAccountMainInfo());
        } else {
            //还款金额 <= 总欠款
            //判断是否为当期本金
            BigDecimal subtractAmt = remainPrinAmt.subtract(repayPrinAmt);
            Predicate<AmAccountMainInvestorInfo> checkCastPrin = acct -> loanAcct.getCastTenor().equals(prinAcct.getBillingTenor())
                    && TransBizConstant.ACCOUNT_TYPE_06.equals(prinAcct.getAccountType()) && investorLoanId.equals(acct.getInvestorLoanId());
            //更新当期的本金交易账户的贷调金额为 总欠款-还款金额
            upDateAcctList.stream()
                    .filter(checkCastPrin)
                    .forEach(
                            acct -> {
                                acct.setCreditAdjustAmount(acct.getCreditAdjustAmount().add(subtractAmt));
                                acct.setCurrBalance(acct.getCurrBalance().subtract(subtractAmt));
                                logger.info("调整当期本金交易账户acct:{}", BeanUtils.toMap(acct));
                            });


            //已抛未还本金需要减去贷调金额
            loanAcct.setPrincipalBalance(loanAcct.getPrincipalBalance().subtract(subtractAmt));
            //已抛未还总金额需要减去贷调金额
            loanAcct.setCurrBalance(loanAcct.getCurrBalance().subtract(subtractAmt));
        }
    }

    /**
     * 获取每期轧差交易账户
     *
     * @param saveBean
     * @param repayMapPerTenor
     * @param offsetLoanMain
     * @param offsetLoanAcct
     * @param businessDate
     * @throws TxnException
     */
    private void getOffsetAccount(InvestorPaymentSaveBean saveBean, Map<String, BigDecimal> repayMapPerTenor, AmLoanMainInvestorInfo offsetLoanMain,
                                  AmLoanAccountInvestorInfo offsetLoanAcct, int tenor, Date businessDate) throws TxnException {
        //获取每期交易账户
        List<AmAccountMainInvestorInfo> needDealAcctList = saveBean.getNeedDealAcctList().stream()
                .filter(acct -> tenor == acct.getBillingTenor()).collect(Collectors.toList());

        //获取每期各个成分欠款
        ToBigDecimalFunction<AmAccountMainInvestorInfo> getAcctDebt = acct -> acct.getPostingAmount().subtract(acct.getRepayAmount()).subtract(acct.getCreditAdjustAmount());
        Map<String, BigDecimal> investorAcctMap = needDealAcctList.stream().collect(
                Collectors.groupingBy(AmAccountMainInvestorInfo::getAccountType, CollectorsUtils.summingBigDecimal(getAcctDebt))
        );

        //生成各个成分轧差费用交易账户，并且将生成的费用交易账户加入到各个成分的分组
        if (null == repayMapPerTenor) {
            return;
        }

        for (Map.Entry<String, BigDecimal> entry : repayMapPerTenor.entrySet()) {
            String accoutType = entry.getKey();
            if (accoutType.equals(TransBizConstant.ACCOUNT_TYPE_06)) {
                continue;
            }
            //比较欠款和还款额大小
            BigDecimal repayAmt = entry.getValue();
            BigDecimal debtAmt = Optional.ofNullable(investorAcctMap.get(accoutType)).orElse(BigDecimal.ZERO);
            //对客贴息还款与联合贷利息欠款比较
            if (accoutType.equals(TransBizConstant.ACCOUNT_TYPE_17)) {
                debtAmt = Optional.ofNullable(investorAcctMap.get(TransBizConstant.ACCOUNT_TYPE_04)).orElse(BigDecimal.ZERO);
            }
            if (repayAmt.compareTo(debtAmt) <= 0) {
                continue;
            }
            BigDecimal offsetAmt = repayAmt.subtract(debtAmt);

            String feeCode = "";
            switch (accoutType) {
                case TransBizConstant.ACCOUNT_TYPE_04:
                case TransBizConstant.ACCOUNT_TYPE_17: {
                    feeCode = TransBizConstant.TXN_CODE_OFFSET_INTR_FEE;
                    break;
                }
                case TransBizConstant.ACCOUNT_TYPE_11: {
                    feeCode = TransBizConstant.TXN_CODE_OFFSET_PENT_FEE;
                    break;
                }
                case TransBizConstant.ACCOUNT_TYPE_12: {
                    feeCode = TransBizConstant.TXN_CODE_OFFSET_COMP_FEE;
                    break;
                }
                default:
                    feeCode = TransBizConstant.TXN_CODE_OFFSET_FEE_FEE;
            }

            InvestorCastAccountTransactionBean intrCastAccrt = accountService.castOffsetFeeAccount(offsetLoanMain, offsetLoanAcct,
                    offsetAmt, tenor, feeCode, businessDate);

            saveBean.addSaveGlList(intrCastAccrt.getTlGlInterfaceInfoList());
            saveBean.addSaveAcct(intrCastAccrt.getAmAccountMainInfo());
            saveBean.addAcctGroupByTypeMap(intrCastAccrt.getAmAccountMainInfo(), accoutType.equals(TransBizConstant.ACCOUNT_TYPE_17) ? TransBizConstant.ACCOUNT_TYPE_04 : accoutType);
        }
    }


    /**
     * 轧差，冲账
     *
     * @param saveBean
     * @param repayMap
     * @param loanMainList
     * @param loanAcctList
     * @param bussinessDate
     * @throws TxnException
     */
    private void repaymentAllocation(InvestorPaymentSaveBean saveBean, Map<Integer, Map<String, BigDecimal>> repayMap,
                                     List<AmLoanMainInvestorInfo> loanMainList, List<AmLoanAccountInvestorInfo> loanAcctList, Date bussinessDate) throws TxnException {

        //08交易账户
        TlPaymentAllocationInvestorLog totalAllocationLog = saveBean.getSavaPaymentLog();
        List<AmAccountMainInvestorInfo> allAcctList = new ArrayList<>();
        List<AmAccountMainInvestorInfo> updateAcctList = new ArrayList<>();
        List<AmAccountMainInvestorInfo> saveAcctList = new ArrayList<>();


        //获取轧差方订单信息
        AmLoanMainInvestorInfo offsetLoanMain = loanMainList.stream()
                .filter(loan -> TransBizConstant.OFFSET_1.equals(loan.getOffsetFlag())).
                        findFirst().get();
        //获取轧差方订单账户
        AmLoanAccountInvestorInfo offsetLoanAcct = loanAcctList.stream()
                .filter(loan -> offsetLoanMain.getInvestorLoanId().equals(loan.getInvestorLoanId()))
                .findFirst().get();


        //获取分组交易账户
        Map<Integer, Map<String, List<AmAccountMainInvestorInfo>>> acctGroupByTenorMap = saveBean.getAcctGroupByTenorMap();

        //部分还款，修复先息后本本金无法关闭的问题 即联合贷有本金抛帐金额为0、但是无还款本金的问题
        acctGroupByTenorMap.keySet().forEach(tenor -> {
                    repayMap.computeIfAbsent(tenor, k -> new HashMap<>());
                }
        );

        //轮询每个期次
        for (Map.Entry<Integer, Map<String, BigDecimal>> repayMapEntry : repayMap.entrySet()) {
            int tenor = repayMapEntry.getKey();
            Map<String, BigDecimal> repayMapByTenor = repayMapEntry.getValue();

            //处理轧差
            this.getOffsetAccount(saveBean, repayMapByTenor, offsetLoanMain, offsetLoanAcct, tenor, bussinessDate);


            //原来需要更新交易账户
            List<AmAccountMainInvestorInfo> oriUpdateAcctPerTenorList = saveBean.getUpdateAcctList().stream()
                    .filter(acct -> tenor == acct.getBillingTenor()).collect(Collectors.toList());
            //原来需要新增交易账户
            List<AmAccountMainInvestorInfo> oriSaveAcctPerTenorList = saveBean.getSaveAcctList().stream()
                    .filter(acct -> tenor == acct.getBillingTenor()).collect(Collectors.toList());
            //需要更新每一期的本，利，罚，费交易账户
            List<AmAccountMainInvestorInfo> updateAcctPerTenorList = new ArrayList<>();
            //需要新增每一期的本，利，罚，费交易账户
            List<AmAccountMainInvestorInfo> saveAcctPerTenorList = new ArrayList<>();
            //轮询每个成分
            Map<String, List<AmAccountMainInvestorInfo>> acctGroupByTypeMap = acctGroupByTenorMap.get(tenor);
            for (Map.Entry<String, List<AmAccountMainInvestorInfo>> entry : acctGroupByTypeMap.entrySet()) {
                String accoutType = entry.getKey();
                //获取每个成分需要处理的交易账户
                List<AmAccountMainInvestorInfo> needDealAcctList = entry.getValue();
                //进行还款顺序排序
                needDealAcctList = this.sortByPaymentSequence(needDealAcctList, loanMainList);

                //冲账金额
                BigDecimal paymentAmt = Optional.ofNullable(repayMapByTenor.get(accoutType)).orElse(BigDecimal.ZERO);
                //冲账利息时需要加上贴息的还款
                if (TransBizConstant.ACCOUNT_TYPE_04.equals(accoutType)) {
                    paymentAmt = paymentAmt.add(Optional.ofNullable(repayMapByTenor.get(TransBizConstant.ACCOUNT_TYPE_17)).orElse(BigDecimal.ZERO));
                }

                //开始分配
                for (AmAccountMainInvestorInfo needDealAcct : needDealAcctList) {
                    logger.info("开始分配交易账户orgiAcct:{},还款金额:{}", BeanUtils.toMap(needDealAcct), paymentAmt);
                    //单笔欠款
                    BigDecimal acctRemainAmnt = needDealAcct.getCurrBalance();
                    logger.info("单笔欠款acctRemainAmnt:{}", acctRemainAmnt);
                    //还款金额已分配完
                    if (paymentAmt.compareTo(BigDecimal.ZERO) <= 0 && acctRemainAmnt.compareTo(BigDecimal.ZERO) > 0) {
                        logger.info("还款金额小于0，结束分配");
                        break;
                    }

                    //真正子交易还款金额
                    BigDecimal realPaymentAmnt;
                    //还款金额<=欠款金额,生成一笔还款子交易，金额为还款金额
                    if (paymentAmt.compareTo(acctRemainAmnt) <= 0) {
                        realPaymentAmnt = paymentAmt;
                        logger.info("还款金额<=欠款金额，生成一笔还款子交易，金额为还款金额realPaymentAmnt:{}", realPaymentAmnt);
                    } else {
                        // 还款金额>欠款金额，生成一笔还款子交易，金额为欠款金额
                        realPaymentAmnt = acctRemainAmnt;
                        logger.info("还款金额>欠款金额，生成一笔还款子交易，金额为欠款金额realPaymentAmnt:{}", realPaymentAmnt);
                    }
                    paymentAmt = paymentAmt.subtract(realPaymentAmnt);

                    //生成对应的09交易账户  添加代偿标识判断
                    TlPaymentAllocationInvestorLog paymentLog = investorTlPaymentAllocationLogService.buildTlPaymentAllocationLog(totalAllocationLog,
                            needDealAcct, realPaymentAmnt);
                    saveBean.addPaymentLog(paymentLog);


                    //关闭原交易账户
                    logger.info("关闭原交易账户");
                    needDealAcct.setRepayAmount(needDealAcct.getRepayAmount().add(realPaymentAmnt));
                    needDealAcct.setCurrBalance(needDealAcct.getCurrBalance().subtract(realPaymentAmnt));
                    needDealAcct.setLastPaymentTime(DateUtils.getBusinessTime(new Date(), bussinessDate));
                    if (TransBizConstant.COMPENSATORY_FLAG_1.equals(totalAllocationLog.getCompensatoryFlag())) {
                        needDealAcct.setCompensatoryFlag(TransBizConstant.COMPENSATORY_FLAG_1);
                        if (needDealAcct.getCompensatoryDays() == null) {
                            needDealAcct.setCompensatoryDays(0);
                        } else {
                            int cmpsNumber = needDealAcct.getCompensatoryDays() + 1;
                            needDealAcct.setCompensatoryDays(cmpsNumber);
                        }
                    }

                    if (TransBizConstant.REPAYMENT_TYPE_PRETERMINTION.equals(totalAllocationLog.getPaymentType())) {
                        needDealAcct.setAccountStatus(TransBizConstant.ACCOUNT_STATUS_7);
                    } else {
                        needDealAcct.setAccountStatus(TransBizConstant.ACCOUNT_STATUS_8);
                    }


                    //轮询原更新队列，如果存在AccountId相同，则更新，否则应该新增
                    Predicate<AmAccountMainInvestorInfo> isSameAcctId = acct -> needDealAcct.getAccountId().equals(acct.getAccountId());
                    long count = oriUpdateAcctPerTenorList.stream().filter(isSameAcctId).count();
                    if (count > 0) {
                        logger.info("需要更新orgiAcct:{}", BeanUtils.toMap(needDealAcct));
                        updateAcctPerTenorList.add(needDealAcct);
                        oriUpdateAcctPerTenorList.removeIf(isSameAcctId);
                    } else {
                        logger.info("需要新增orgiAcct:{}", BeanUtils.toMap(needDealAcct));
                        saveAcctPerTenorList.add(needDealAcct);
                        oriSaveAcctPerTenorList.removeIf(isSameAcctId);
                    }
                }
            }


            //重新设置需要保存的交易账户
            saveAcctPerTenorList.addAll(oriSaveAcctPerTenorList);
            saveAcctList.addAll(saveAcctPerTenorList);
            //重新设置需要更新的交易账户
            updateAcctPerTenorList = this.copyUpdateAcctList(updateAcctPerTenorList);
            updateAcctList.addAll(updateAcctPerTenorList);

            List<AmAccountMainInvestorInfo> allAcctPerTenorList = new ArrayList<>();
            allAcctPerTenorList.addAll(saveAcctPerTenorList);
            allAcctPerTenorList.addAll(updateAcctPerTenorList);
            allAcctPerTenorList.addAll(oriUpdateAcctPerTenorList);
            allAcctList.addAll(allAcctPerTenorList);
        }
        saveBean.setSaveAcctList(saveAcctList);
        saveBean.setUpdateAcctList(updateAcctList);
        saveBean.setAllAcctList(allAcctList);

    }

    /**
     * 进行还款顺序排序
     *
     * @param needDealAcctList
     * @param loanMainInvestorInfoList
     * @return
     */
    private List<AmAccountMainInvestorInfo> sortByPaymentSequence(List<AmAccountMainInvestorInfo> needDealAcctList, List<AmLoanMainInvestorInfo> loanMainInvestorInfoList) {
        //构建辅助排序队列
        Function<AmAccountMainInvestorInfo, InvestorAmAccountMainInfoSortBean> createSortBean = acct -> {
            InvestorAmAccountMainInfoSortBean sortBean = new InvestorAmAccountMainInfoSortBean();
            sortBean.setAmAccountMainInvestorInfo(acct);
            String offset = loanMainInvestorInfoList.stream().filter(loan -> loan.getInvestorLoanId().equals(acct.getInvestorLoanId()))
                    .findFirst().get().getOffsetFlag();
            sortBean.setOffset(offset);
            return sortBean;
        };
        //按轧差方最后冲账排序
        return needDealAcctList.stream()
                .map(createSortBean)
                .sorted(Comparator.comparing(InvestorAmAccountMainInfoSortBean::getOffset))
                .map(InvestorAmAccountMainInfoSortBean::getAmAccountMainInvestorInfo)
                .collect(Collectors.toList());
    }

    /**
     * 生成需要更新的交易账户
     *
     * @param orgiAcctList 原始账户列表
     * @return 交易账户list
     */
    private List<AmAccountMainInvestorInfo> copyUpdateAcctList(List<AmAccountMainInvestorInfo> orgiAcctList) {
        UnaryOperator<AmAccountMainInvestorInfo> copyAcct = orgiAcct -> {
            AmAccountMainInvestorInfo acct = new AmAccountMainInvestorInfo();
            acct.setId(orgiAcct.getId());
            acct.setInvestorLoanId(orgiAcct.getInvestorLoanId());
            acct.setTenantId(orgiAcct.getTenantId());
            acct.setCustomerId(orgiAcct.getCustomerId());
            acct.setBillingTenor(orgiAcct.getBillingTenor());
            acct.setRepayAmount(orgiAcct.getRepayAmount());
            acct.setCreditAdjustAmount(orgiAcct.getCreditAdjustAmount());
            acct.setCurrBalance(orgiAcct.getCurrBalance());
            acct.setAccountStatus(orgiAcct.getAccountStatus());
            acct.setLastPaymentTime(orgiAcct.getLastPaymentTime());
            acct.setCompensatoryFlag(orgiAcct.getCompensatoryFlag());
            acct.setCompensatoryDays(orgiAcct.getCompensatoryDays());
            return acct;
        };
        return orgiAcctList.stream().map(copyAcct).collect(Collectors.toList());
    }

    /**
     * 将原saveBean 按订单拆分生成新的saveBean
     *
     * @param saveBean
     * @param loanMain
     * @return
     */
    private InvestorPaymentSaveBean afterAllocation(InvestorPaymentSaveBean saveBean, AmLoanMainInvestorInfo loanMain) {
        String investorLoanId = loanMain.getInvestorLoanId();
        InvestorPaymentSaveBean saveBeanOrder = new InvestorPaymentSaveBean();
        saveBeanOrder.setSavaPaymentLog(saveBean.getSavaPaymentLog());
        saveBeanOrder.addPaymentLogList(saveBean.getPaymentLogSaveList().stream().filter(log -> investorLoanId.equals(log.getInvestorLoanId())).collect(Collectors.toList()));
        saveBeanOrder.setSaveAcctList(saveBean.getSaveAcctList().stream().filter(acct -> investorLoanId.equals(acct.getInvestorLoanId())).collect(Collectors.toList()));
        saveBeanOrder.setUpdateAcctList(saveBean.getUpdateAcctList().stream().filter(acct -> investorLoanId.equals(acct.getInvestorLoanId())).collect(Collectors.toList()));
        saveBeanOrder.setAllAcctList(saveBean.getAllAcctList().stream().filter(acct -> investorLoanId.equals(acct.getInvestorLoanId())).collect(Collectors.toList()));
        saveBeanOrder.setSaveAccuAcctList(saveBean.getSaveAccuAcctList().stream().filter(accuAcct -> investorLoanId.equals(accuAcct.getInvestorLoanId())).collect(Collectors.toList()));
        saveBeanOrder.setUpdateAccuAcctList(saveBean.getUpdateAccuAcctList().stream().filter(accuAcct -> investorLoanId.equals(accuAcct.getInvestorLoanId())).collect(Collectors.toList()));
        saveBeanOrder.setSaveGlList(saveBean.getSaveGlList().stream().filter(gl -> investorLoanId.equals(gl.getInvestorLoanId())).collect(Collectors.toList()));
        logger.info("saveBeanOrder:{}", BeanUtils.toMap(saveBeanOrder));
        return saveBeanOrder;
    }

    /**
     * 处理还款计划
     *
     * @param saveBean
     * @param planList
     * @param bussinessDate
     */
    private void dealPlan(InvestorPaymentSaveBean saveBean, List<AmRepaymentPlanInvestorInfo> planList, Date bussinessDate) {
        logger.info("更新还款计划开始");
        //09还款交易子账户
        List<TlPaymentAllocationInvestorLog> savePaymentLogList = saveBean.getPaymentLogSaveList();
        //所有处理和未处理的交易账户
        List<AmAccountMainInvestorInfo> allAcctList = saveBean.getAllAcctList();
        //08还款交易子账户
        TlPaymentAllocationInvestorLog totalPaymentLog = saveBean.getSavaPaymentLog();
        //先按期分组
        Map<Integer, List<TlPaymentAllocationInvestorLog>> repayAmtListMap = savePaymentLogList.stream().collect(Collectors.groupingBy(TlPaymentAllocationInvestorLog::getBillingTenor));
        repayAmtListMap.forEach(
                //每期按关联账户类型分组求每期还款本金，还款利息，还款罚息，还款费用分别之和
                (tenor, paymentLogList) -> {
                    logger.info("更新还款计划,第{}期", tenor);
                    Map<String, BigDecimal> repayMap = paymentLogList.stream().collect(
                            Collectors.groupingBy(TlPaymentAllocationInvestorLog::getConnectAccountType, CollectorsUtils.summingBigDecimal(TlPaymentAllocationInvestorLog::getPostingAmount))
                    );
                    logger.info("更新还款计划,该期的欠款:{}", repayMap);
                    //更新每期还款计划状态，还款本金，还款利息，还款罚息
                    AmRepaymentPlanInvestorInfo repayPlan = planList.stream().filter(plan -> tenor.equals(plan.getBillingTenor())).findFirst().orElse(null);
                    if (null == repayPlan) {
                        return;
                    }
                    logger.info("更新还款计划,plan:{}", BeanUtils.toMap(repayPlan));

                    AmRepaymentPlanInvestorInfo updatePlan = new AmRepaymentPlanInvestorInfo();
                    updatePlan.setId(repayPlan.getId());
                    updatePlan.setCustomerId(repayPlan.getCustomerId());
                    updatePlan.setTenantId(repayPlan.getTenantId());
                    updatePlan.setBillingTenor(repayPlan.getBillingTenor());
                    updatePlan.setPaymentDueDate(repayPlan.getPaymentDueDate());
                    updatePlan.setLiabilityFlag(repayPlan.getLiabilityFlag());

                    BigDecimal prinAmt = Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_06)).orElse(BigDecimal.ZERO);
                    updatePlan.setRepayPrincipal(repayPlan.getRepayPrincipal().add(prinAmt));

                    BigDecimal intrAmt = Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_04)).orElse(BigDecimal.ZERO);
                    updatePlan.setRepayInterest(repayPlan.getRepayInterest().add(intrAmt));

                    BigDecimal pentAmt = Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_11)).orElse(BigDecimal.ZERO);
                    updatePlan.setRepayPenaltyInterest(repayPlan.getRepayPenaltyInterest().add(pentAmt));


                    BigDecimal compoundAmt = Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_12)).orElse(BigDecimal.ZERO);
                    updatePlan.setRepayCompoundInterest(repayPlan.getRepayCompoundInterest().add(compoundAmt));


                    //获取当期分期费用之和
                    BigDecimal repayFee = paymentLogList.stream()
                            .filter(log -> TransBizConstant.TXN_CODE_INST_PYMT_FEE.equals(log.getTxnCode()))
                            .map(TlPaymentAllocationInvestorLog::getPostingAmount)
                            .reduce(BigDecimal.ZERO, BigDecimal::add);
                    updatePlan.setRepayFee(repayPlan.getRepayFee().add(repayFee));

                    //判断交易账户状态为正常或者逾期
                    Predicate<AmAccountMainInvestorInfo> acctNotClosed =
                            acct -> TransBizConstant.ACCOUNT_STATUS_1.equals(acct.getAccountStatus())
                                    || TransBizConstant.ACCOUNT_STATUS_2.equals(acct.getAccountStatus());
                    //判断是否为该期次账户
                    Predicate<AmAccountMainInvestorInfo> isSameTenor = acct -> tenor.equals(acct.getBillingTenor());
                    //轮询当期没有关闭的交易账户数量，如果当期没有正常或者逾期的交易账户，即当期如果交易已全部关闭
                    long acctCount = allAcctList.stream().filter(acctNotClosed.and(isSameTenor)).count();
                    if (acctCount <= 0) {
                        if (TransBizConstant.REPAYMENT_TYPE_PRETERMINTION.equals(totalPaymentLog.getPaymentType())
                                || TransBizConstant.REPAYMENT_TYPE_PREPAY.equals(totalPaymentLog.getPaymentType())
                                || TransBizConstant.REPAYMENT_TYPE_CRT.equals(totalPaymentLog.getPaymentType())) {
                            //提前结清
                            updatePlan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_7);
                            updatePlan.setOverDueAmount(BigDecimal.ZERO);
                            updatePlan.setPayoffDate(bussinessDate);
                        } else if (TransBizConstant.REPAYMENT_TYPE_STAGING.equals(totalPaymentLog.getPaymentType())) {
                            //账单分期
                            updatePlan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_6);
                            updatePlan.setOverDueAmount(BigDecimal.ZERO);
                            updatePlan.setPayoffDate(bussinessDate);
                        } else {
                            //正常结清
                            updatePlan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_8);
                            updatePlan.setOverDueAmount(BigDecimal.ZERO);
                            updatePlan.setPayoffDate(bussinessDate);
                        }
                    }
                    updatePlan.setTransactionDate(bussinessDate);
                    if (TransBizConstant.LOAN_PLAN_STATUS_2.equals(repayPlan.getPlanStatus())) {
                        BigDecimal totalAmt = repayPlan.getTotalAmount().subtract(repayPlan.getRepayPrincipal()).subtract(repayPlan.getRepayInterest())
                                .subtract(repayPlan.getRepayFee());
                        updatePlan.setOverDueAmount(totalAmt);
                    }
                    //添加代偿标识判断
                    if (TransBizConstant.COMPENSATORY_FLAG_1.equals(totalPaymentLog.getCompensatoryFlag())) {
                        updatePlan.setCompensateFlag(TransBizConstant.COMPENSATORY_FLAG_1);
                    }
                    logger.info("开始更新还款计划,更新后的还款计划updatePlan:{}", updatePlan);
                    saveBean.addUpdatePlan(updatePlan);
                }
        );
        logger.info("更新还款计划结束");
    }

    /**
     * 处理提前还款之后的还款计划
     *
     * @param saveBean
     * @param loanMainInfo
     * @param loanAccountInfo
     * @param planList
     * @param unOffSetReplanList
     * @param prinBalMap
     * @param prProductTable
     * @param bussinessDate
     * @throws TxnException
     */
    private void dealPrePayPlan(InvestorPaymentSaveBean saveBean, AmLoanMainInvestorInfo loanMainInfo, AmLoanAccountInvestorInfo loanAccountInfo,
                                List<AmRepaymentPlanInvestorInfo> planList, List<AmRepaymentPlanInvestorInfo> unOffSetReplanList,
                                Map<String, BigDecimal> prinBalMap, PrProductTable prProductTable, Date bussinessDate) throws TxnException {
        logger.info("处理提前还款后的还款计划");
        //还款后剩余本金 = 欠款本金-还款本金
        BigDecimal afterPayPrinBalAmt = prinBalMap.get(loanMainInfo.getInvestorLoanId());
        logger.info("订单还款后剩余本金afterPayPrinBalAmt:{}", afterPayPrinBalAmt);
        //如果还款本金>=剩余本金
        if (afterPayPrinBalAmt.compareTo(BigDecimal.ZERO) <= 0) {
            //提前结清
            this.dealPlan(saveBean, planList, bussinessDate);
        } else {
            //还款后剩余本金
            //提前部分还款
            this.cancelPlan(saveBean, planList);
            this.reBuildPlan(saveBean, loanMainInfo, loanAccountInfo, afterPayPrinBalAmt, unOffSetReplanList, prProductTable, bussinessDate);
        }
    }

    /**
     * 取消原还款计划
     *
     * @param saveBean 保存本金利息罚息dto对象
     */
    private void cancelPlan(InvestorPaymentSaveBean saveBean, List<AmRepaymentPlanInvestorInfo> planList) {
        //所有需要处理的还款计划
        //获取更新实体
        UnaryOperator<AmRepaymentPlanInvestorInfo> cancelPlan = plan -> {
            AmRepaymentPlanInvestorInfo updatePlan = new AmRepaymentPlanInvestorInfo();
            updatePlan.setId(plan.getId());
            updatePlan.setCustomerId(plan.getCustomerId());
            updatePlan.setTenantId(plan.getTenantId());
            updatePlan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_9);
            updatePlan.setBillingTenor(plan.getBillingTenor());
            return updatePlan;
        };

        //需要更新还款计划
        planList.stream()
                .filter(plan -> TransBizConstant.LOAN_PLAN_STATUS_1.equals(plan.getPlanStatus()) || TransBizConstant.LOAN_PLAN_STATUS_0.equals(plan.getPlanStatus()))
                .map(cancelPlan)//取消原还款计划
                .forEach(saveBean::addUpdatePlan);

    }

    /**
     * 重新生成还款计划
     *
     * @param saveBean
     * @param loanMainInfo
     * @param loanAccountInfo
     * @param afterPayPrinBalAmt
     * @param unOffSetReplanList
     * @param prProductTable
     * @param bussinessDate
     * @throws TxnException
     */
    private void reBuildPlan(InvestorPaymentSaveBean saveBean, AmLoanMainInvestorInfo loanMainInfo, AmLoanAccountInvestorInfo loanAccountInfo,
                             BigDecimal afterPayPrinBalAmt, List<AmRepaymentPlanInvestorInfo> unOffSetReplanList, PrProductTable prProductTable, Date bussinessDate) throws TxnException {
        logger.info("处理重新生成还款计划");
        Integer tenor = loanMainInfo.getTotalTenor() - loanAccountInfo.getCastTenor() + 1;
        //构建生成还款计划请求Bean，
        // 使用订单下个还款日作为重建还款计划的首期还款日，重建还款计划不需要计算首期还款计划
        if (TransBizConstant.LOAN_DAY_PAY_FLAG_1.equals(prProductTable.getLoanDayPayFlag()) && bussinessDate.compareTo(loanMainInfo.getPostingDate()) == 0) {
            logger.info("当天借当天还且当天提前还款");
            bussinessDate = DateUtils.addDay(bussinessDate, 1);
        }

        InvestorLoanPlanParamBean paramBean = new InvestorLoanPlanParamBean();
        paramBean.setFirstPaymentDate(loanAccountInfo.getNextPaymentDate());
        paramBean.setBusinessDate(bussinessDate);
        paramBean.setRepaymentPlanInvestorInfos(unOffSetReplanList);
        loanMainInfo.setTotalTenor(tenor);
        loanMainInfo.setLoanAmount(afterPayPrinBalAmt);

        //重新生成还款计划
        List<AmRepaymentPlanInvestorInfo> amRepaymentPlanInfoList = repaymentPlanService.buildRepaymentPlanInvestor(paramBean, loanMainInfo, loanAccountInfo);

        amRepaymentPlanInfoList.forEach(
                plan -> {
                    plan.setBillingTenor(plan.getBillingTenor() + loanAccountInfo.getCastTenor() - 1);//将还款计划设置为正确的期数
                    saveBean.addSavePlan(plan);
                }
        );
        unOffSetReplanList.addAll(amRepaymentPlanInfoList);
    }

    /**
     * 根据重新生成的还款计划，对当期本金，利息累计，分期费用重新抛帐
     *
     * @param saveBean        保存本金利息罚息dto对象
     * @param loanMainInfo    贷款信息
     * @param loanAccountInfo 贷款账户信息
     * @param bussinessDate   业务日期
     * @throws TxnException 自定义异常
     */
    private void reCastAcct(InvestorPaymentSaveBean saveBean, AmLoanMainInvestorInfo loanMainInfo, AmLoanAccountInvestorInfo loanAccountInfo, Date bussinessDate) throws TxnException {

        //判断是否有新生成的还款计划  有 提前部分还款  没有 提前结清
        if (CollectionUtils.isEmpty(saveBean.getSavePlanList())) {//没有直接返回 不做任何处理
            return;
        }

        String investorLoanId = loanMainInfo.getInvestorLoanId();
        List<AmRepaymentPlanInvestorInfo> planInfoList = saveBean.getSavePlanList().stream()
                .filter(plan -> investorLoanId.equals(plan.getInvestorLoanId()))
                .collect(Collectors.toList());

        if (CollectionUtils.isEmpty(planInfoList)) {//没有直接返回 不做任何处理
            return;
        }

        logger.info("重新抛帐,loanMainInfo:{},loanAccountInfo:{}", BeanUtils.toMap(loanMainInfo), BeanUtils.toMap(loanAccountInfo));
        if (!TransBizConstant.LOAN_STATUS_1.equals(loanAccountInfo.getAccountStatus())) {
            logger.info("订单已结清");
            return;
        }

        //提前还款场景特殊，此时抛帐需要生成当期的交易账户，所以在这里特殊处理
        loanAccountInfo.setCastTenor(loanAccountInfo.getCastTenor() - 1);

        //本金,分期费用，利息累计抛帐
        InvestorCastAccountBean castAccountBean = accountService.castTxnAccount(loanMainInfo, loanAccountInfo, planInfoList, bussinessDate, false);
        saveBean.addSaveAcctList(castAccountBean.getAccountMainInfoList());
        saveBean.addSaveAccuAcctList(castAccountBean.getInterestAccrualInfoList());
        saveBean.addSaveGlList(castAccountBean.getGlInterfaceInfoList());
    }

    /**
     * 处理订单信息
     *
     * @param saveBean
     * @param loanAccountInvestorInfo
     * @param planList
     */
    private void dealOrder(InvestorPaymentSaveBean saveBean, AmLoanAccountInvestorInfo loanAccountInvestorInfo,
                           List<AmRepaymentPlanInvestorInfo> planList) {
        logger.info("更新订单开始,loanAccountInfo:{}", BeanUtils.toMap(loanAccountInvestorInfo));
        // 获取订单信息
        AmLoanMainInvestorInfo amLoanMainInvestorInfo = extAmLoanMainInvestorInfoMapper.selectByInvestorLoanIdAndCustId(loanAccountInvestorInfo.getInvestorLoanId(), loanAccountInvestorInfo.getCustomerId(), loanAccountInvestorInfo.getTenantId());
        //09还款交易账户
        List<TlPaymentAllocationInvestorLog> paymentLogList = saveBean.getPaymentLogSaveList();
        //已处理的还款计划
        List<AmRepaymentPlanInvestorInfo> updatePlanList = saveBean.getUpdatePlanList();
        //08还款交易账户
        TlPaymentAllocationInvestorLog totalPaymentLog = saveBean.getSavaPaymentLog();

        BigDecimal totalBalAmt = loanAccountInvestorInfo.getCurrBalance().subtract(totalPaymentLog.getPostingAmount());
        AmLoanAccountInvestorInfo updateLoan = new AmLoanAccountInvestorInfo();
        updateLoan.setId(loanAccountInvestorInfo.getId());
        updateLoan.setTenantId(loanAccountInvestorInfo.getTenantId());
        updateLoan.setCustomerId(loanAccountInvestorInfo.getCustomerId());
        updateLoan.setAccruedInterest(loanAccountInvestorInfo.getAccruedInterest());
        updateLoan.setAccruedPenalty(loanAccountInvestorInfo.getAccruedPenalty());
        updateLoan.setAccruedCompound(loanAccountInvestorInfo.getAccruedCompound());
        updateLoan.setNextPaymentDate(loanAccountInvestorInfo.getNextPaymentDate());
        updateLoan.setCastTenor(loanAccountInvestorInfo.getCastTenor());
        //只有回购才更新标识 代偿不更新
        if (totalPaymentLog.getPaymentType().equals(TransBizConstant.REPAYMENT_TYPE_CRT)) {
            updateLoan.setCompensatoryFlag(loanAccountInvestorInfo.getCompensatoryFlag());//代偿标识
        }
        updateLoan.setCurrBalance(totalBalAmt);
        //还款子交易账户按关联交易账户类型分组求本金，利息，罚息之和   key->账户类型，value->分组之和
        Map<String, BigDecimal> repayMap = paymentLogList.stream().collect(
                Collectors.groupingBy(TlPaymentAllocationInvestorLog::getConnectAccountType, CollectorsUtils.summingBigDecimal(TlPaymentAllocationInvestorLog::getPostingAmount))
        );
        logger.info("还款分组repayMap:{}", repayMap);

        BigDecimal prinAmt = Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_06)).orElse(BigDecimal.ZERO);
        //更新所有已抛未还本金余额
        BigDecimal principalBalance = loanAccountInvestorInfo.getPrincipalBalance().subtract(prinAmt);
        updateLoan.setPrincipalBalance(principalBalance);
        //更新所有剩余本金
        BigDecimal remainPricipal = loanAccountInvestorInfo.getRemainPrincipal().subtract(prinAmt);
        updateLoan.setRemainPrincipal(remainPricipal);


        //更新利息余额
        BigDecimal intrAmt = Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_04)).orElse(BigDecimal.ZERO);
        BigDecimal interestBalance = loanAccountInvestorInfo.getInterestBalance().subtract(intrAmt);
        updateLoan.setInterestBalance(interestBalance);


        //更新罚息余额
        BigDecimal pentAmt = Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_11)).orElse(BigDecimal.ZERO);
        BigDecimal penaltyBalance = loanAccountInvestorInfo.getPenaltyBalance().subtract(pentAmt);
        updateLoan.setPenaltyBalance(penaltyBalance);


        //更新罚息复利余额
        BigDecimal compoundAmt = Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_12)).orElse(BigDecimal.ZERO);
        BigDecimal compoundBalance = loanAccountInvestorInfo.getCompoundBalance().subtract(compoundAmt);
        updateLoan.setCompoundBalance(compoundBalance);


        //获取分期费用之和
        BigDecimal repayFee = paymentLogList.stream()
                .filter(log -> TransBizConstant.TXN_CODE_INST_PYMT_FEE.equals(log.getTxnCode()))
                .map(TlPaymentAllocationInvestorLog::getPostingAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        //更新分期费用余额
        BigDecimal feeBalance = loanAccountInvestorInfo.getFeeBalance().subtract(repayFee);
        updateLoan.setFeeBalance(feeBalance);

        //更新总余额
        BigDecimal totalCurrBalance = loanAccountInvestorInfo.getCurrBalance().subtract(prinAmt).subtract(intrAmt).subtract(repayFee);
        updateLoan.setCurrBalance(totalCurrBalance);

        //如果不存在没有还完的还款计划，则订单已结清
        Predicate<AmRepaymentPlanInvestorInfo> notRepayPlan = planInfo -> TransBizConstant.LOAN_PLAN_STATUS_0.equals(planInfo.getPlanStatus())
                || TransBizConstant.LOAN_PLAN_STATUS_1.equals(planInfo.getPlanStatus())
                || TransBizConstant.LOAN_PLAN_STATUS_2.equals(planInfo.getPlanStatus())
                || TransBizConstant.LOAN_PLAN_STATUS_9.equals(planInfo.getPlanStatus());
        long repayTenorCount = saveBean.getUpdatePlanList().stream()
                .filter(notRepayPlan.negate())
                .count();
        long notRepayTenorCount = planList.stream()
                .filter(notRepayPlan)
                .count();
        int maxTener = saveBean.getUpdatePlanList().stream()
                .filter(notRepayPlan.negate())
                .map(AmRepaymentPlanInvestorInfo::getBillingTenor)
                .max(Integer::compareTo)
                .orElse(0);
        //如果还完期数 = 原始剩余未还期数
        if (repayTenorCount == notRepayTenorCount) {
            if (TransBizConstant.REPAYMENT_TYPE_PRETERMINTION.equals(totalPaymentLog.getPaymentType())
                    || TransBizConstant.REPAYMENT_TYPE_PREPAY.equals(totalPaymentLog.getPaymentType())
                    || TransBizConstant.REPAYMENT_TYPE_CRT.equals(totalPaymentLog.getPaymentType())
                    || TransBizConstant.REPAYMENT_TYPE_PREPAY_CURR.equals(totalPaymentLog.getPaymentType())) {
                updateLoan.setCastTenor(loanAccountInvestorInfo.getCastTenor());
                updateLoan.setAccountStatus(TransBizConstant.LOAN_STATUS_7);//提前结清
                updateLoan.setPayoffDate(DateUtils.format(totalPaymentLog.getPostingDate(), DateUtils.DATE_PATTERN_ANOTHER));
            } else if (TransBizConstant.REPAYMENT_TYPE_STAGING.equals(totalPaymentLog.getPaymentType())) {
                updateLoan.setCastTenor(loanAccountInvestorInfo.getCastTenor());
                updateLoan.setAccountStatus(TransBizConstant.LOAN_STATUS_6);//提前结清
                updateLoan.setPayoffDate(DateUtils.format(totalPaymentLog.getPostingDate(), DateUtils.DATE_PATTERN_ANOTHER));
            } else {
                //正常还款，逾期还款如果还最后一期，订单更新未正常结清
                updateLoan.setAccountStatus(TransBizConstant.LOAN_STATUS_8);//正常结清
                updateLoan.setPayoffDate(DateUtils.format(totalPaymentLog.getPostingDate(), DateUtils.DATE_PATTERN_ANOTHER));
            }
        } else if (maxTener == loanAccountInvestorInfo.getCastTenor() - 1 || maxTener == amLoanMainInvestorInfo.getTotalTenor()) {
            //如果还完最大期数 = 当前已抛期数-1，更新为正常
            updateLoan.setAccountStatus(TransBizConstant.LOAN_STATUS_1);
        }

        //订单未结清才更新连续逾期天数
        if (TransBizConstant.LOAN_STATUS_1.equals(loanAccountInvestorInfo.getAccountStatus())) {
            updateLoan.setContinueOverDueTenor(0);
        }

        //更新总代偿次数
        if (TransBizConstant.REPAYMENT_TYPE_CMPS.equals(totalPaymentLog.getPaymentType())) {
            if (loanAccountInvestorInfo.getTotalCompensatoryTenor() == null) {
                updateLoan.setTotalCompensatoryTenor(1);
            } else {
                int totalNumber = loanAccountInvestorInfo.getTotalCompensatoryTenor() + 1;
                updateLoan.setTotalCompensatoryTenor(totalNumber);//总代偿次数
            }
        }

        //获取最大延滞天数
        planList.stream()
                .filter(plan -> plan.getBillingTenor() > maxTener)
                .filter(plan -> TransBizConstant.LOAN_PLAN_STATUS_2.equals(plan.getPlanStatus()))
                .map(AmRepaymentPlanInvestorInfo::getDelqDays)
                .max(Integer::compareTo)
                .ifPresent(delqMaxDays -> {
                    if (!delqMaxDays.equals(loanAccountInvestorInfo.getDelqDays())) {
                        updateLoan.setDelqDays(delqMaxDays);
                    }
                });


        //这次还完的期数
        Predicate<AmRepaymentPlanInvestorInfo> repayPlan = planInfo -> TransBizConstant.LOAN_PLAN_STATUS_6.equals(planInfo.getPlanStatus())
                || TransBizConstant.LOAN_PLAN_STATUS_7.equals(planInfo.getPlanStatus())
                || TransBizConstant.LOAN_PLAN_STATUS_8.equals(planInfo.getPlanStatus());
        long payedTenor = updatePlanList.stream().filter(repayPlan).count();
        int payedTenorNow = loanAccountInvestorInfo.getPayedTenor() + (int) payedTenor;
        updateLoan.setPayedTenor(payedTenorNow);
        logger.info("更新后的订单,loanAccountInfo:{}", BeanUtils.toMap(loanAccountInvestorInfo));
        saveBean.setUpdateLoan(updateLoan);
    }

    /**
     * 处理延滞
     *
     * @param saveBean
     */
    private void dealDelq(InvestorPaymentSaveBean saveBean, AmLoanMainInvestorInfo loanMainInvestorInfo, AmLoanAccountInvestorInfo loanAccountInvestorInfo,
                          List<AmDelinquencyInvestorInfo> delqAcctList, DelqParamBean delqParamBean, Date bussinessDate) throws TxnException {
        //处理延滞交易账户
        this.updateDelqAcct(saveBean, loanMainInvestorInfo, loanAccountInvestorInfo, delqAcctList, delqParamBean, bussinessDate);
        //还未关闭的订单写入会计待处理列表
        this.delqAccounting(saveBean, loanAccountInvestorInfo, bussinessDate);
    }

    /**
     * 处理延滞交易账户
     *
     * @param saveBean
     * @param amLoanMainInvestorInfo
     * @param amLoanAccountInvestorInfo
     * @param delqAcctList
     * @param delqParamBean
     * @param bussinessDate
     * @throws TxnException
     */
    private void updateDelqAcct(InvestorPaymentSaveBean saveBean, AmLoanMainInvestorInfo amLoanMainInvestorInfo, AmLoanAccountInvestorInfo amLoanAccountInvestorInfo, List<AmDelinquencyInvestorInfo> delqAcctList, DelqParamBean delqParamBean, Date bussinessDate) throws TxnException {
        logger.info("处理延滞交易账户开始");
        List<AmDelinquencyInvestorInfo> delqList = delqAcctList.stream().filter(acct -> amLoanMainInvestorInfo.getInvestorLoanId().equals(acct.getInvestorLoanId()))
                .collect(Collectors.toList());

        PrInvestorPenaltyRate penaltyRateTable = penaltyRateService.findPrInvestorPenaltyRate(amLoanMainInvestorInfo.getTenantId(), amLoanMainInvestorInfo.getInvestorPenaltyTableId());
        UncheckedConsumer<AmRepaymentPlanInvestorInfo> dealDelq = plan -> delqList.stream()
                .filter(delq -> delq.getBillingTenor().equals(plan.getBillingTenor()))
                .findFirst()
                .ifPresent(
                        delq -> {
                            logger.info("处理延滞交易账户delq:{}", BeanUtils.toMap(delq));

                            if (TransBizConstant.LOAN_PLAN_STATUS_7.equals(plan.getPlanStatus()) ||
                                    TransBizConstant.LOAN_PLAN_STATUS_8.equals(plan.getPlanStatus())) {
                                AmDelinquencyInvestorInfo updateDelq = new AmDelinquencyInvestorInfo();
                                updateDelq.setId(delq.getId());
                                updateDelq.setCustomerId(delq.getCustomerId());
                                updateDelq.setTenantId(delq.getTenantId());
                                updateDelq.setAccountStatus(TransBizConstant.ACCOUNT_STATUS_8);
                                saveBean.addUpdateDelq(updateDelq);
                            }

                            if (TransBizConstant.LOAN_PLAN_STATUS_2.equals(plan.getPlanStatus())) {
                                try {
                                    List<AmInterestAccrualInvestorInfo> accuAcctList = accountService.castPenAndComAccrualAccount(plan, amLoanMainInvestorInfo, amLoanAccountInvestorInfo,
                                            penaltyRateTable, delqParamBean, bussinessDate);
                                    saveBean.addSaveAccuAcctList(accuAcctList);
                                } catch (Exception e) {
                                    throw new RuntimeException(e);
                                }

                                AmDelinquencyInvestorInfo updateDelq = new AmDelinquencyInvestorInfo();
                                updateDelq.setId(delq.getId());
                                updateDelq.setCustomerId(delq.getCustomerId());
                                updateDelq.setTenantId(delq.getTenantId());
                                updateDelq.setTotalAmountDue(plan.getOverDueAmount());
                                logger.info("处理延滞交易账户updateDelq:{}", BeanUtils.toMap(updateDelq));
                                saveBean.addUpdateDelq(updateDelq);
                            }
                        });

        //循环处理每期延滞
        saveBean.getUpdatePlanList().forEach(CollectorsUtils.tranToUncheck(dealDelq));
    }

    /**
     * 记录会计状态转列待处理列表
     *
     * @param saveBean
     * @param amLoanAccountInvestorInfo
     * @param bussinessDate
     */
    private void delqAccounting(InvestorPaymentSaveBean saveBean, AmLoanAccountInvestorInfo amLoanAccountInvestorInfo, Date bussinessDate) throws TxnBizException {
        if (TransBizConstant.LOAN_STATUS_7.equals(amLoanAccountInvestorInfo.getAccountStatus())
                || TransBizConstant.LOAN_STATUS_8.equals(amLoanAccountInvestorInfo.getAccountStatus())) {
            return;
        }
        String investorLoanId = amLoanAccountInvestorInfo.getInvestorLoanId();
        String customerId = amLoanAccountInvestorInfo.getCustomerId();
        String tenantId = amLoanAccountInvestorInfo.getTenantId();
        int count = extBtInvestorAccountingTransferListMapper.selectCountTransferList(investorLoanId, customerId, tenantId, bussinessDate);
        if (count > 0) {
            return;
        }
        BtInvestorAccountingTransferList transferList = btInvestorAccountingTransferListService.build(amLoanAccountInvestorInfo, bussinessDate);
        saveBean.addTransferList(transferList);
        logger.info("记录会计状态转列待处理列表transferList:{}", BeanUtils.toMap(transferList));
    }

    /**
     * 生成清分流水
     *
     * @param saveBean
     * @param loanMain
     */
    private void dealTransLog(InvestorPaymentSaveBean saveBean, AmLoanMainInvestorInfo loanMain, PrInvestorTable table, Date bussinessDate) throws SegmentException, TxnException {
        if (TransBizConstant.DEFAULT_INVESTOR_ID.equals(loanMain.getInvestorLoanId())) {
            return;
        }

        //09还款交易账户
        List<TlPaymentAllocationInvestorLog> paymentLogList = saveBean.getPaymentLogSaveList();
        BigDecimal transAmt = paymentLogList.stream().map(TlPaymentAllocationInvestorLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        TlFundTransferLog log = new TlFundTransferLog();
        BeanMapping.copy(loanMain, log);
        log.setBusinessDate(bussinessDate);
        log.setId(segmentService.getId(TransBizConstant.TL_FUND_TRANSFER_LOG));
        String seqNo = TransBizConstant.PAYMENT_LOG_SEQ_PREFIX + SeqUtils.getSNByLength(TransBizConstant.PAYMENT_LOG_SEQ_RADOM_LENGTH);
        log.setSeqNo(seqNo);
        log.setRefNo(sequenceNumberService.generateRefNo(Constant.SEQUENCE_LENGTH));
        log.setAmount(transAmt);
        log.setTransferType(TransBizConstant.TRANSFER_TYPE_3);
        log.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_D);
        log.setNarrative(table.getChineseName() + "还款清分");
        log.setLaunchTime(DateUtils.getBusinessTime(new Date(), bussinessDate));
        log.setCreateTime(null);
        log.setUpdateTime(null);
        log.setGrayVersion(grayVersionConfig.getCurrentVersion());
        saveBean.addTransLog(log);

        // 构建贷方的资金转账明细
        TlFundTransferDetail investorDetl = new TlFundTransferDetail();
        BeanMapping.copy(log, investorDetl);
        investorDetl.setId(segmentService.getId(TransBizConstant.TL_FUND_TRANSFER_DETAIL));
        investorDetl.setTransferSeqNo(seqNo);
        investorDetl.setAcctNo(table.getPrepaidAccount());
        investorDetl.setCrDrInd(TransBizConstant.CR_DR_IND_C);
        investorDetl.setNarrative(table.getChineseName() + "贷记");
        investorDetl.setCurrency(TransBizConstant.CURRENCY_CNY);
        investorDetl.setAmount(transAmt);
        investorDetl.setInvestorId(loanMain.getInvestorId());
        saveBean.addDetl(investorDetl);

        // 构建借方的资金转账明细
        SystemParameterRes systemParameter = systemParameterService.findByParamCode(TransBizConstant.THIRD_ACCT_NO, loanMain.getTenantId());
        TlFundTransferDetail investorDetl2 = new TlFundTransferDetail();
        BeanMapping.copy(log, investorDetl2);
        investorDetl2.setId(segmentService.getId(TransBizConstant.TL_FUND_TRANSFER_DETAIL));
        investorDetl2.setTransferSeqNo(seqNo);
        investorDetl2.setAcctNo(systemParameter.getParamValue());
        investorDetl2.setCrDrInd(TransBizConstant.CR_DR_IND_D);
        investorDetl2.setNarrative(table.getChineseName() + "借记");
        investorDetl2.setCurrency(TransBizConstant.CURRENCY_CNY);
        investorDetl2.setAmount(transAmt);
        saveBean.addDetl(investorDetl2);

    }

    /**
     * 保存所有还款信息
     *
     * @param saveBean 保存本金利息罚息dto对象
     */
    private void saveAllAccts(InvestorPaymentSaveBean saveBean) {
        TlPaymentAllocationInvestorLog savaPaymentLog = saveBean.getSavaPaymentLog();//需要保存的还款总交易账户
        //需要保存的还款子交易账户
        List<TlPaymentAllocationInvestorLog> paymentLogSaveList = saveBean.getPaymentLogSaveList().stream()
                .filter(log -> log.getPostingAmount().compareTo(BigDecimal.ZERO) > 0)
                .collect(Collectors.toList());
        paymentLogSaveList.add(savaPaymentLog);
        extTlPaymentAllocationInvestorLogMapper.batchInsert(paymentLogSaveList);


        List<AmInterestAccrualInvestorInfo> saveAccuAcctList = saveBean.getSaveAccuAcctList();//需要保存的累计账户
        if (CollectionUtils.isNotEmpty(saveAccuAcctList)) {
            extAmInterestAccrualInvestorInfoMapper.batchInsertInterestAccrualInvestorInfo(saveAccuAcctList);
        }

        List<AmInterestAccrualInvestorInfo> updateAccuAcctList = saveBean.getUpdateAccuAcctList();//需要更新的累计账户
        if (CollectionUtils.isNotEmpty(updateAccuAcctList)) {
            extAmInterestAccrualInvestorInfoMapper.batchUpdate(updateAccuAcctList);
        }

        List<AmAccountMainInvestorInfo> saveAcctList = saveBean.getSaveAcctList();//保存的交易账户
        if (CollectionUtils.isNotEmpty(saveAcctList)) {
            extAmAccountMainInvestorInfoMapper.batchInsertAccount(saveAcctList);
        }
        List<AmAccountMainInvestorInfo> updateAcctList = saveBean.getUpdateAcctList();//需要更新交易账户
        if (CollectionUtils.isNotEmpty(updateAcctList)) {
            extAmAccountMainInvestorInfoMapper.batchUpdate(updateAcctList);
        }

        List<TlGlInterfaceInvestorInfo> glSaveList = saveBean.getSaveGlList();//需要保存的gl流水
        if (CollectionUtils.isNotEmpty(glSaveList)) {
            extTlGlInterfaceInvestorInfoMapper.batchInsertGl(glSaveList);
        }

        List<AmRepaymentPlanInvestorInfo> savePlanList = saveBean.getSavePlanList();//需要保存的还款计划
        if (CollectionUtils.isNotEmpty(savePlanList)) {
            extAmRepaymentPlanInvestorInfoMapper.batchInsertRepaymentPlanInfoList(savePlanList);
        }

        List<AmRepaymentPlanInvestorInfo> updatePlanList = saveBean.getUpdatePlanList();//需要更新的还款计划
        if (CollectionUtils.isNotEmpty(updatePlanList)) {
            extAmRepaymentPlanInvestorInfoMapper.batchUpdate(updatePlanList);
        }

        List<AmDelinquencyInvestorInfo> updateDelqList = saveBean.getUpdateDelqList();////需要更新的延滞交易账户
        if (CollectionUtils.isNotEmpty(updateDelqList)) {
            extAmDelinquencyInvestorInfoMapper.batchUpdate(updateDelqList);
        }

        List<AmLoanAccountInvestorInfo> updateLoanList = saveBean.getUpdateLoanList();//需要更新的订单
        if (CollectionUtils.isNotEmpty(updateLoanList)) {
            extAmLoanAccountInvestorInfoMapper.batchUpdate(updateLoanList);
        }

        List<BtInvestorAccountingTransferList> transferLists = saveBean.getTransferLists();
        if (CollectionUtils.isNotEmpty(transferLists)) {
            extBtInvestorAccountingTransferListMapper.batchInsert(transferLists);
        }

        List<TlFundTransferLog> transferLogList = saveBean.getTransLogList();
        if (CollectionUtils.isNotEmpty(transferLogList)) {
            extTlFundTransferLogMapper.batchInsert(transferLogList);
        }

        List<TlFundTransferDetail> detailList = saveBean.getDetailList();
        if (CollectionUtils.isNotEmpty(detailList)) {
            extTlFundTransferDetailMapper.batchInsert(detailList);
        }
    }

    /**
     * 恢复出资方额度
     *
     * @param paymentLogList
     * @param loanMainList
     */
    private void recoverInvestorLimit(List<TlPaymentAllocationInvestorLog> paymentLogList, List<AmLoanMainInvestorInfo> loanMainList) {

        if (CollectionUtils.isEmpty(paymentLogList) || CollectionUtils.isEmpty(loanMainList)) {
            return;
        }

        Map<String, BigDecimal> investorAmtMap = paymentLogList.stream().filter(log -> TransBizConstant.ACCOUNT_TYPE_06.equals(log.getConnectAccountType()))
                .collect(Collectors.groupingBy(TlPaymentAllocationInvestorLog::getInvestorId, CollectorsUtils.summingBigDecimal(TlPaymentAllocationInvestorLog::getPostingAmount)));

        for (AmLoanMainInvestorInfo loanMain : loanMainList) {
            BigDecimal amount = investorAmtMap.get(loanMain.getInvestorId());
            if (amount == null || BigDecimal.ZERO.compareTo(amount) == 0) {
                continue;
            }
            HandleInvestorLimitReq req = new HandleInvestorLimitReq(loanMain.getTenantId(), loanMain.getInvestorLoanId(), loanMain.getChannel(), loanMain.getInvestorId(), loanMain.getProductId(), amount, Constant.INVESTOR_LIMIT_CREDITTYPE_L, Constant.INVESTOR_LIMIT_USETYPE_R);
            anyTxnParamService.handleInvestorLimit(req);
        }
    }

    /**
     * 代偿还款还款分配
     *
     * @param paymentLog
     * @param loanMainList
     * @param bussinessDate
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealCmpsPay(TlPaymentInvestorLog paymentLog, List<AmLoanMainInvestorInfo> loanMainList, Date bussinessDate) throws Exception {
        String loanId = paymentLog.getLoanId();
        String customerId = paymentLog.getCustomerId();
        String tenantId = paymentLog.getTenantId();
        String custSeqNo = paymentLog.getCustPaymentSeqNo();

        //获取所有资金方贷款订单账户
        List<AmLoanAccountInvestorInfo> loanAcctList = extAmLoanAccountInvestorInfoMapper.findInvestorLoan(loanId, customerId, tenantId);
        //获取所有资金方还款计划
        List<String> planStatusList = Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_2);
        List<AmRepaymentPlanInvestorInfo> planList = extAmRepaymentPlanInvestorInfoMapper.selectByLoanIdAndStatus(loanId, planStatusList, customerId, tenantId);
        //获取所有资金方的交易账户
        List<String> statusList = Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_2);
        List<String> typeList = Arrays.asList(TransBizConstant.ACCOUNT_TYPE_03, TransBizConstant.ACCOUNT_TYPE_04,
                TransBizConstant.ACCOUNT_TYPE_06, TransBizConstant.ACCOUNT_TYPE_11, TransBizConstant.ACCOUNT_TYPE_12);
        List<AmAccountMainInvestorInfo> acctList = extAmAccountMainInvestorInfoMapper.selectAccounts(loanId, typeList, customerId, tenantId, statusList);
        //获取所有资金方的累计账户
        List<String> accuAcctTypeList = Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_10, TransBizConstant.INTEREST_ACCRUAL_TYPE_20);
        List<AmInterestAccrualInvestorInfo> accuAcctList = extAmInterestAccrualInvestorInfoMapper.selectAllAccuAcct(
                loanId, customerId, tenantId, accuAcctTypeList, TransBizConstant.ACCOUNT_STATUS_1);
        //获取所有资金方的延滞账户
        List<AmDelinquencyInvestorInfo> delqAcctList = extAmDelinquencyInvestorInfoMapper.selectByLoanIdAndStatus(loanId, customerId, tenantId, TransBizConstant.ACCOUNT_STATUS_1);
        DelqParamBean delqParamBean = dealDelqService.buildDqlqAccountBean(loanMainList.get(0).getTenantId(), loanMainList.get(0).getDelqTableId());


        //生成08交易账户
        TlPaymentAllocationInvestorLog paymentAllocationInvestorLog = investorTlPaymentAllocationLogService.buildTlPaymentAllocationInvestorLog(paymentLog,
                loanMainList.get(0), bussinessDate);

        InvestorPaymentSaveBean saveBean = new InvestorPaymentSaveBean();
        saveBean.setSavaPaymentLog(paymentAllocationInvestorLog);
        //轮询每一个资金源订单获取相应账户
        for (AmLoanMainInvestorInfo loanMain : loanMainList) {
            String investorLoanId = loanMain.getInvestorLoanId();
            AmLoanAccountInvestorInfo loanAcct = loanAcctList.stream()
                    .filter(loan -> loan.getInvestorLoanId().equals(investorLoanId))
                    .findFirst()
                    .orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMain.getInvestorLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });
            loanAcct.setCompensatoryFlag(TransBizConstant.COMPENSATORY_FLAG_1);//代偿标识

            //获取交易账户
            this.getAlreadyCastAcct(saveBean, loanMain, acctList);

            //累计账户抛帐
            this.castAccuAcct(saveBean, loanMain, loanAcct, accuAcctList, bussinessDate);
        }

        //获取对客09还款交易账户
        List<TlPaymentAllocationLog> custlog = tlPaymentAllocationLogService.findCustLog(loanId, custSeqNo, customerId, tenantId);
        //获取每期，每个成分的还款
        Map<Integer, Map<String, BigDecimal>> repayMap = custlog.stream()
                .collect(Collectors.groupingBy(TlPaymentAllocationLog::getBillingTenor,
                        Collectors.groupingBy(TlPaymentAllocationLog::getConnectAccountType,
                                CollectorsUtils.summingBigDecimal(TlPaymentAllocationLog::getPostingAmount))));

        //轧差，冲账
        this.repaymentAllocation(saveBean, repayMap, loanMainList, loanAcctList, bussinessDate);

        //生成09对应的gl流水
        List<TlGlInterfaceInvestorInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList());
        saveBean.addSaveGlList(saveGlList);


        InvestorPaymentSaveBean saveBeanCustOrder = new InvestorPaymentSaveBean();
        //循环订单做还款分配后处理
        for (AmLoanMainInvestorInfo loanMain : loanMainList) {
            String investorLoanId = loanMain.getInvestorLoanId();
            AmLoanAccountInvestorInfo loanAcct = loanAcctList.stream().filter(loan -> loan.getInvestorLoanId().equals(investorLoanId)).findFirst().orElseThrow(() -> {
                logger.error("订单交易账户为空，loanId:{}", loanMain.getInvestorLoanId());
                return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
            });
            loanAcct.setCompensatoryFlag(TransBizConstant.COMPENSATORY_FLAG_1);//代偿标识

            List<AmRepaymentPlanInvestorInfo> planInfoList = planList.stream()
                    .filter(plan -> investorLoanId.equals(plan.getInvestorLoanId()))
                    .collect(Collectors.toList());

            //生成有后续每个订单处理的saveBean
            InvestorPaymentSaveBean saveBeanPerOrder = this.afterAllocation(saveBean, loanMain);

            // 处理还款计划
            this.dealPlan(saveBeanPerOrder, planInfoList, bussinessDate);

            // 处理订单
            this.dealOrder(saveBeanPerOrder, loanAcct, planInfoList);

            // 处理延滞
            this.dealDelq(saveBeanPerOrder, loanMain, loanAcct, delqAcctList, delqParamBean, bussinessDate);

            PrInvestorTable table = investorTableService.findInvestorByInvestorId(loanMain.getInvestorId(), tenantId);
            // 处理合作行清分
            this.dealTransLog(saveBeanPerOrder, loanMain, table, bussinessDate);

            saveBeanCustOrder.add(saveBeanPerOrder);
        }

        // 入库
        this.saveAllAccts(saveBeanCustOrder);

        // 还款时恢复出资方的日可用额度和总可用额度
        this.recoverInvestorLimit(saveBean.getPaymentLogSaveList(), loanMainList);
    }

    /**
     * 回购还款还款分配
     *
     * @param paymentLog
     * @param loanMainList
     * @param bussinessDate
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealCrtPay(TlPaymentInvestorLog paymentLog, List<AmLoanMainInvestorInfo> loanMainList, Date bussinessDate) throws Exception {
        String loanId = paymentLog.getLoanId();
        String customerId = paymentLog.getCustomerId();
        String tenantId = paymentLog.getTenantId();
        String custSeqNo = paymentLog.getCustPaymentSeqNo();

        //获取所有资金方贷款订单账户
        List<AmLoanAccountInvestorInfo> loanAcctList = extAmLoanAccountInvestorInfoMapper.findInvestorLoan(loanId, customerId, tenantId);
        //获取所有资金方还款计划
        List<String> planStatusList = Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2);
        List<AmRepaymentPlanInvestorInfo> planList = extAmRepaymentPlanInvestorInfoMapper.selectByLoanIdAndStatus(loanId, planStatusList, customerId, tenantId);
        //获取所有资金方的交易账户
        List<String> statusList = Arrays.asList(TransBizConstant.ACCOUNT_STATUS_1, TransBizConstant.ACCOUNT_STATUS_2);
        List<String> typeList = Arrays.asList(TransBizConstant.ACCOUNT_TYPE_03, TransBizConstant.ACCOUNT_TYPE_04,
                TransBizConstant.ACCOUNT_TYPE_06, TransBizConstant.ACCOUNT_TYPE_11, TransBizConstant.ACCOUNT_TYPE_12);
        List<AmAccountMainInvestorInfo> acctList = extAmAccountMainInvestorInfoMapper.selectAccounts(loanId, typeList, customerId, tenantId, statusList);
        //获取所有资金方的累计账户
        List<String> accuAcctTypeList = Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_66, TransBizConstant.INTEREST_ACCRUAL_TYPE_10, TransBizConstant.INTEREST_ACCRUAL_TYPE_20);
        List<AmInterestAccrualInvestorInfo> accuAcctList = extAmInterestAccrualInvestorInfoMapper.selectAllAccuAcct(
                loanId, customerId, tenantId, accuAcctTypeList, TransBizConstant.ACCOUNT_STATUS_1);
        //获取所有资金方的延滞账户
        List<AmDelinquencyInvestorInfo> delqAcctList = extAmDelinquencyInvestorInfoMapper.selectByLoanIdAndStatus(loanId, customerId, tenantId, TransBizConstant.ACCOUNT_STATUS_1);
        DelqParamBean delqParamBean = dealDelqService.buildDqlqAccountBean(loanMainList.get(0).getTenantId(), loanMainList.get(0).getDelqTableId());


        //生成08交易账户
        TlPaymentAllocationInvestorLog paymentAllocationInvestorLog = investorTlPaymentAllocationLogService.buildTlPaymentAllocationInvestorLog(paymentLog,
                loanMainList.get(0), bussinessDate);

        InvestorPaymentSaveBean saveBean = new InvestorPaymentSaveBean();
        saveBean.setSavaPaymentLog(paymentAllocationInvestorLog);
        //轮询每一个资金源订单获取相应账户
        for (AmLoanMainInvestorInfo loanMain : loanMainList) {
            String investorLoanId = loanMain.getInvestorLoanId();
            AmLoanAccountInvestorInfo loanAcct = loanAcctList.stream()
                    .filter(loan -> loan.getInvestorLoanId().equals(investorLoanId))
                    .findFirst()
                    .orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMain.getInvestorLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });
            loanAcct.setCompensatoryFlag(TransBizConstant.COMPENSATORY_FLAG_1);//代偿标识

            //获取交易账户
            this.getAlreadyCastAcct(saveBean, loanMain, acctList);

            //累计账户抛帐
            this.castAccuAcct(saveBean, loanMain, loanAcct, accuAcctList, bussinessDate);

            // 未抛帐的本金抛帐
            List<AmRepaymentPlanInvestorInfo> planInfoList = planList.stream()
                    .filter(plan -> investorLoanId.equals(plan.getInvestorLoanId()))
                    .collect(Collectors.toList());
            this.castNonCastPrinAcct(saveBean, loanMain, loanAcct, planInfoList, delqParamBean, bussinessDate);
        }

        //获取对客09还款交易账户
        List<TlPaymentAllocationLog> custlog = tlPaymentAllocationLogService.findCustLog(loanId, custSeqNo, customerId, tenantId);
        //获取每期，每个成分的还款
        Map<Integer, Map<String, BigDecimal>> repayMap = custlog.stream()
                .collect(Collectors.groupingBy(TlPaymentAllocationLog::getBillingTenor,
                        Collectors.groupingBy(TlPaymentAllocationLog::getConnectAccountType,
                                CollectorsUtils.summingBigDecimal(TlPaymentAllocationLog::getPostingAmount))));

        //轧差，冲账
        this.repaymentAllocation(saveBean, repayMap, loanMainList, loanAcctList, bussinessDate);

        //生成09对应的gl流水
        List<TlGlInterfaceInvestorInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList());
        saveBean.addSaveGlList(saveGlList);


        InvestorPaymentSaveBean saveBeanCustOrder = new InvestorPaymentSaveBean();
        //循环订单做还款分配后处理
        for (AmLoanMainInvestorInfo loanMain : loanMainList) {
            String investorLoanId = loanMain.getInvestorLoanId();
            AmLoanAccountInvestorInfo loanAcct = loanAcctList.stream().filter(loan -> loan.getInvestorLoanId().equals(investorLoanId)).findFirst().orElseThrow(() -> {
                logger.error("订单交易账户为空，loanId:{}", loanMain.getInvestorLoanId());
                return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
            });
            loanAcct.setCompensatoryFlag(TransBizConstant.COMPENSATORY_FLAG_1);//代偿标识

            List<AmRepaymentPlanInvestorInfo> planInfoList = planList.stream()
                    .filter(plan -> investorLoanId.equals(plan.getInvestorLoanId()))
                    .collect(Collectors.toList());

            //生成有后续每个订单处理的saveBean
            InvestorPaymentSaveBean saveBeanPerOrder = this.afterAllocation(saveBean, loanMain);

            // 处理还款计划
            this.dealPlan(saveBeanPerOrder, planInfoList, bussinessDate);

            // 处理订单
            this.dealOrder(saveBeanPerOrder, loanAcct, planInfoList);

            // 处理延滞
            this.dealDelq(saveBeanPerOrder, loanMain, loanAcct, delqAcctList, delqParamBean, bussinessDate);

            PrInvestorTable table = investorTableService.findInvestorByInvestorId(loanMain.getInvestorId(), tenantId);
            // 处理合作行清分
            this.dealTransLog(saveBeanPerOrder, loanMain, table, bussinessDate);

            saveBeanCustOrder.add(saveBeanPerOrder);
        }

        // 入库
        this.saveAllAccts(saveBeanCustOrder);

        // 还款时恢复出资方的日可用额度和总可用额度
        this.recoverInvestorLimit(saveBean.getPaymentLogSaveList(), loanMainList);
    }
}
